diff --git a/gtk2_ardour/sfdb_freesound_mootcher.cc b/gtk2_ardour/sfdb_freesound_mootcher.cc index 12682ca00f..9311c73776 100644 --- a/gtk2_ardour/sfdb_freesound_mootcher.cc +++ b/gtk2_ardour/sfdb_freesound_mootcher.cc @@ -2,11 +2,12 @@ Adapted for Ardour by Ben Loftis, March 2008 Updated to new Freesound API by Colin Fletcher, November 2011 + Updated to Freesound API v2 by Colin Fletcher, May 2022 Mootcher 23-8-2005 Mootcher Online Access to thefreesoundproject website - http://freesound.iua.upf.edu/ + http://freesound.org/ GPL 2005 Jorn Lemon mail for questions/remarks: mootcher@twistedlemon.nl @@ -53,19 +54,35 @@ #include "pbd/i18n.h" #include "ardour/audio_library.h" +#include "ardour/debug.h" +#include "ardour/filesystem_paths.h" #include "ardour/rc_configuration.h" #include "pbd/pthread_utils.h" +#include "pbd/openuri.h" + +#include "ardour_dialog.h" #include "gui_thread.h" +#include "widgets/prompter.h" using namespace PBD; -static const std::string base_url = "http://www.freesound.org/api"; -static const std::string api_key = "9d77cb8d841b4bcfa960e1aae62224eb"; // ardour3 +// freesound API URLs are always https://, and don't include the www. subdomain +static const std::string base_url = "https://freesound.org/apiv2"; + +// Ardour 7 +static const std::string default_token = "t3TjQ67WNh6zJLZRnWmArSiZ8bKlgTc2aEsV1cP7"; +static const std::string client_id = "yesyr1g4StTtg2F50KT1"; + +static const std::string fields = "id,name,duration,filesize,samplerate,license,download,previews"; //------------------------------------------------------------------------ -Mootcher::Mootcher() +Mootcher::Mootcher(const std::string &the_token) : curl(curl_easy_init()) { + DEBUG_TRACE(PBD::DEBUG::Freesound, "Created new Mootcher, oauth_token =\"" + the_token + "\"\n"); + custom_headers = NULL; + oauth_token = the_token; + cancel_download_btn.set_label (_("Cancel")); progress_hbox.pack_start (progress_bar, true, true); progress_hbox.pack_end (cancel_download_btn, false, false); @@ -77,6 +94,10 @@ Mootcher::Mootcher() Mootcher:: ~Mootcher() { curl_easy_cleanup(curl); + if (custom_headers) { + curl_slist_free_all (custom_headers); + } + DEBUG_TRACE(PBD::DEBUG::Freesound, "Destroyed Mootcher\n"); } //------------------------------------------------------------------------ @@ -85,6 +106,7 @@ void Mootcher::ensureWorkingDir () { std::string p = ARDOUR::Config->get_freesound_download_dir(); + DEBUG_TRACE(PBD::DEBUG::Freesound, "ensureWorkingDir() - " + p + "\n"); if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) { if (g_mkdir_with_parents (p.c_str(), 0775) != 0) { PBD::error << "Unable to create Mootcher working dir" << endmsg; @@ -127,15 +149,15 @@ std::string Mootcher::sortMethodString(enum sortMethod sort) // sort the results in the requested way. switch (sort) { - case sort_duration_desc: return "duration_desc"; - case sort_duration_asc: return "duration_asc"; - case sort_created_desc: return "created_desc"; - case sort_created_asc: return "created_asc"; - case sort_downloads_desc: return "downloads_desc"; - case sort_downloads_asc: return "downloads_asc"; - case sort_rating_desc: return "rating_desc"; - case sort_rating_asc: return "rating_asc"; - default: return ""; + case sort_duration_descending: return "duration_desc"; + case sort_duration_ascending: return "duration_asc"; + case sort_created_descending: return "created_desc"; + case sort_created_ascending: return "created_asc"; + case sort_downloads_descending: return "downloads_desc"; + case sort_downloads_ascending: return "downloads_asc"; + case sort_rating_descending: return "rating_desc"; + case sort_rating_ascending: return "rating_asc"; + default: return ""; } } @@ -152,8 +174,6 @@ void Mootcher::setcUrlOptions() // Allow connections to time out (without using signals) curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30); - - } std::string Mootcher::doRequest(std::string uri, std::string params) @@ -167,24 +187,24 @@ std::string Mootcher::doRequest(std::string uri, std::string params) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &xml_page); - // curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); - // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str()); - // curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1); - // the url to get std::string url = base_url + uri + "?"; if (params != "") { - url += params + "&api_key=" + api_key + "&format=xml"; + url += params + "&token=" + default_token + "&format=xml"; } else { - url += "api_key=" + api_key + "&format=xml"; + url += "token=" + default_token + "&format=xml"; } curl_easy_setopt(curl, CURLOPT_URL, url.c_str() ); + DEBUG_TRACE(PBD::DEBUG::Freesound, "doRequest() " + url + "\n"); + // perform online request CURLcode res = curl_easy_perform(curl); if( res != 0 ) { - error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg; + std::string errmsg = string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)); + error << errmsg << endmsg; + DEBUG_TRACE(PBD::DEBUG::Freesound, errmsg + "\n"); return ""; } @@ -197,6 +217,7 @@ std::string Mootcher::doRequest(std::string uri, std::string params) xml_page.memory = NULL; xml_page.size = 0; + DEBUG_TRACE(PBD::DEBUG::Freesound, result + "\n"); return result; } @@ -205,55 +226,158 @@ std::string Mootcher::searchSimilar(std::string id) { std::string params = ""; - params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve"; + params += "&fields=" + fields; params += "&num_results=100"; + // XXX should we filter out MP3s here, too? + // XXX and what if there are more than 100 similar sounds? - return doRequest("/sounds/" + id + "/similar", params); + return doRequest("/sounds/" + id + "/similar/", params); } //------------------------------------------------------------------------ + +void +Mootcher::report_login_error(const std::string &msg) +{ + DEBUG_TRACE(PBD::DEBUG::Freesound, "Login failed:" + msg + "\n"); + error << "Freesound login failed: " << msg << endmsg; +} + +bool +Mootcher::get_oauth_token() +{ + std::string oauth_url = base_url + "/oauth2/authorize/?client_id="+client_id+"&response_type=code&state=hello"; + std::string auth_code; + + /* use the user's default browser to get an authorization token */ + if (!PBD::open_uri (oauth_url)) { + report_login_error ("cannot open " + oauth_url); + return false; + } + ArdourWidgets::Prompter token_entry(true); + token_entry.set_prompt(_("Please log in to Freesound in the browser window that's just been opened, and paste the authorization code here")); + token_entry.set_title(_("Authorization Code")); + + token_entry.set_name ("TokenEntryWindow"); + // token_entry.set_size_request (250, -1); + token_entry.set_position (Gtk::WIN_POS_MOUSE); + token_entry.add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT); + token_entry.show (); + + if (token_entry.run () != Gtk::RESPONSE_ACCEPT) + return false; + + token_entry.get_result(auth_code); + if (auth_code == "") + return false; + + oauth_token = auth_code_to_oauth_token(auth_code); + setcUrlOptions(); + + return oauth_token != "";; +} + +std::string Mootcher::auth_code_to_oauth_token(const std::string &auth_code) +{ + struct SfdbMemoryStruct json_page; + json_page.memory = NULL; + json_page.size = 0; + CURLcode res; + + setcUrlOptions(); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &json_page); + + std::string oauth_url = base_url + "/oauth2/access_token/"; + std::string new_oauth_token; + + curl_easy_setopt(curl, CURLOPT_URL, oauth_url.c_str()); + curl_easy_setopt(curl, CURLOPT_POST, 5); + curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, + ("client_id=" + client_id + + "&client_secret=" + default_token + + "&grant_type=authorization_code" + + "&code=" + auth_code).c_str()); + + progress_bar.set_text(_("Fetching Access Token (auth_code=") + auth_code + "..."); + while (gtk_events_pending()) gtk_main_iteration (); // allow the progress bar text to update + + res = curl_easy_perform (curl); + if (res != CURLE_OK) { + if (res != CURLE_ABORTED_BY_CALLBACK) { + report_login_error (string_compose ("curl failed: %1, error=%2", oauth_url, res)); + } + return ""; + } + + if (!json_page.memory) { + report_login_error (string_compose ("curl returned nothing, url=%1!", oauth_url)); + return ""; + } + + std::string access_token_json_str = json_page.memory; + free (json_page.memory); + json_page.memory = NULL; + json_page.size = 0; + + DEBUG_TRACE(PBD::DEBUG::Freesound, access_token_json_str); + + // one of these days ardour's gonna need a proper JSON parser... + size_t token_pos = access_token_json_str.find ("access_token"); + oauth_token = access_token_json_str.substr (token_pos + 16, 30); + + // we've set a bunch of curl options - reset the important ones now + curl_easy_setopt(curl, CURLOPT_POST, 0); + + DEBUG_TRACE(PBD::DEBUG::Freesound, "oauth_token is :" + oauth_token + "\n"); + return oauth_token; +} + std::string Mootcher::searchText(std::string query, int page, std::string filter, enum sortMethod sort) { std::string params = ""; char buf[24]; if (page > 1) { - snprintf(buf, 23, "p=%d&", page); + snprintf(buf, 23, "page=%d&", page); params += buf; } char *eq = curl_easy_escape(curl, query.c_str(), query.length()); - params += "q=\"" + std::string(eq) + "\""; - free(eq); + params += "query=\"" + std::string(eq) + "\""; + curl_free(eq); if (filter != "") { char *ef = curl_easy_escape(curl, filter.c_str(), filter.length()); - params += "&f=" + std::string(ef); - free(ef); + params += "&filter=" + std::string(ef); + curl_free(ef); } if (sort) - params += "&s=" + sortMethodString(sort); + params += "&sort=" + sortMethodString(sort); - params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve"; - params += "&sounds_per_page=100"; + params += "&fields=" + fields; + params += "&page_size=100"; - return doRequest("/sounds/search", params); + return doRequest("/search/text/", params); } //------------------------------------------------------------------------ std::string Mootcher::getSoundResourceFile(std::string ID) { - + /* get the resource file for the sound with given ID. + * return the file name of the sound + */ std::string originalSoundURI; std::string audioFileName; std::string xml; + DEBUG_TRACE(PBD::DEBUG::Freesound, "getSoundResourceFile(" + ID + ")\n"); // download the xmlfile into xml_page - xml = doRequest("/sounds/" + ID, ""); + xml = doRequest("/sounds/" + ID + "/", ""); XMLTree doc; doc.read_buffer( xml.c_str() ); @@ -265,14 +389,15 @@ std::string Mootcher::getSoundResourceFile(std::string ID) return ""; } - if (strcmp(doc.root()->name().c_str(), "response") != 0) { - error << string_compose (_("getSoundResourceFile: root = %1, != response"), doc.root()->name()) << endmsg; + if (strcmp(doc.root()->name().c_str(), "root") != 0) { + error << string_compose (_("getSoundResourceFile: root = %1, != \"root\""), doc.root()->name()) << endmsg; return ""; } - XMLNode *name = freesound->child("original_filename"); + XMLNode *name = freesound->child("name"); // get the file name and size from xml file + // assert (name); if (name) { audioFileName = Glib::build_filename (basePath, ID + "-" + name->child("text")->content()); @@ -285,7 +410,7 @@ std::string Mootcher::getSoundResourceFile(std::string ID) std::vector strings; for (niter = children.begin(); niter != children.end(); ++niter) { XMLNode *node = *niter; - if( strcmp( node->name().c_str(), "resource") == 0 ) { + if( strcmp( node->name().c_str(), "list-item") == 0 ) { XMLNode *text = node->child("text"); if (text) { // std::cerr << "tag: " << text->content() << std::endl; @@ -312,6 +437,7 @@ void * Mootcher::threadFunc() { CURLcode res; + DEBUG_TRACE(PBD::DEBUG::Freesound, "threadFunc\n"); res = curl_easy_perform (curl); fclose (theFile); curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar @@ -323,9 +449,16 @@ CURLcode res; } remove ( (audioFileName+".part").c_str() ); } else { - rename ( (audioFileName+".part").c_str(), audioFileName.c_str() ); - // now download the tags &c. - getSoundResourceFile(ID); + DEBUG_TRACE(PBD::DEBUG::Freesound, string_compose("renaming %1.part to %1\n", audioFileName)); + int r = rename ( (audioFileName+".part").c_str(), audioFileName.c_str() ); + if (r != 0) { + const char *err = strerror(errno); + DEBUG_TRACE(PBD::DEBUG::Freesound, string_compose("rename() failed: %1\n", err)); + assert(0); + } else { + // now download the tags &c. + getSoundResourceFile(ID); + } } return (void *) res; @@ -334,7 +467,6 @@ CURLcode res; void Mootcher::doneWithMootcher() { - // update the sound info pane if the selection in the list box is still us sfb->refresh_display(ID, audioFileName); @@ -347,10 +479,11 @@ freesound_download_thread_func(void *arg) Mootcher *thisMootcher = (Mootcher *) arg; void *res; - // std::cerr << "freesound_download_thread_func(" << arg << ")" << std::endl; + DEBUG_TRACE(PBD::DEBUG::Freesound, string_compose("freesound_download_thread_func(%1)\n", arg)); res = thisMootcher->threadFunc(); - + DEBUG_TRACE(PBD::DEBUG::Freesound, string_compose("threadFunc returns %1\n", res)); thisMootcher->Finished(); /* EMIT SIGNAL */ + DEBUG_TRACE(PBD::DEBUG::Freesound, string_compose("returning from freesound_download_thread_func()\n", res)); return res; } @@ -359,6 +492,9 @@ freesound_download_thread_func(void *arg) bool Mootcher::checkAudioFile(std::string originalFileName, std::string theID) { + // return true if file already exists locally and is larger than 256 + // bytes, false otherwise + DEBUG_TRACE(PBD::DEBUG::Freesound, string_compose("checkAudiofile(%1, %2)\n", originalFileName, theID)); ensureWorkingDir(); ID = theID; audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName); @@ -369,19 +505,27 @@ bool Mootcher::checkAudioFile(std::string originalFileName, std::string theID) fseek (testFile , 0 , SEEK_END); if (ftell (testFile) > 256) { fclose (testFile); + DEBUG_TRACE(PBD::DEBUG::Freesound, "checkAudiofile() - found " + audioFileName + "\n"); return true; } // else file was small, probably an error, delete it - fclose(testFile); - remove( audioFileName.c_str() ); + DEBUG_TRACE(PBD::DEBUG::Freesound, "checkAudiofile() - " + audioFileName + " <= 256 bytes, removing it\n"); + fclose (testFile); + // remove (audioFileName.c_str() ); + rename (audioFileName.c_str(), (audioFileName + ".bad").c_str() ); } + DEBUG_TRACE(PBD::DEBUG::Freesound, "checkAudiofile() - not found " + audioFileName + "\n"); return false; } -bool Mootcher::fetchAudioFile(std::string originalFileName, std::string theID, std::string audioURL, SoundFileBrowser *caller) +bool +Mootcher::fetchAudioFile(std::string originalFileName, std::string theID, std::string audioURL, SoundFileBrowser *caller, std::string &token) { + + DEBUG_TRACE(PBD::DEBUG::Freesound, string_compose("fetchAudiofile(%1, %2, %3, ...)\n", originalFileName, theID, audioURL)); + ensureWorkingDir(); ID = theID; audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName); @@ -389,34 +533,51 @@ bool Mootcher::fetchAudioFile(std::string originalFileName, std::string theID, s if (!curl) { return false; } + + Gtk::VBox *freesound_vbox = dynamic_cast (caller->notebook.get_nth_page(2)); + freesound_vbox->pack_start(progress_hbox, Gtk::PACK_SHRINK); + + cancel_download = false; + curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar + curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback); + curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, this); + + if (oauth_token == "") { + if (!get_oauth_token()) { + DEBUG_TRACE(PBD::DEBUG::Freesound, "get_oauth_token() failed!\n"); + return false; + } + } + + token = oauth_token; + // now download the actual file theFile = g_fopen( (audioFileName + ".part").c_str(), "wb" ); if (!theFile) { + DEBUG_TRACE(PBD::DEBUG::Freesound, "Can't open file for writing:" + audioFileName + ".part\n"); return false; } // create the download url - audioURL += "?api_key=" + api_key; + audioURL += "?token=" + default_token; setcUrlOptions(); + std::string auth_header = "Authorization: Bearer " + oauth_token; + DEBUG_TRACE(PBD::DEBUG::Freesound, "auth_header = " + auth_header + "\n"); + custom_headers = curl_slist_append (custom_headers, auth_header.c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, custom_headers); curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() ); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite); curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile); + DEBUG_TRACE(PBD::DEBUG::Freesound, "Downloading audio from " + audioURL + " into " + audioFileName + ".part\n"); std::string prog; prog = string_compose (_("%1"), originalFileName); progress_bar.set_text(prog); - - Gtk::VBox *freesound_vbox = dynamic_cast (caller->notebook.get_nth_page(2)); - freesound_vbox->pack_start(progress_hbox, Gtk::PACK_SHRINK); progress_hbox.show(); - cancel_download = false; - sfb = caller; - curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar - curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback); - curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, this); + sfb = caller; Progress.connect(*this, invalidator (*this), boost::bind(&Mootcher::updateProgress, this, _1, _2), gui_context()); Finished.connect(*this, invalidator (*this), boost::bind(&Mootcher::doneWithMootcher, this), gui_context()); @@ -433,7 +594,7 @@ Mootcher::updateProgress(double dlnow, double dltotal) { if (dltotal > 0) { double fraction = dlnow / dltotal; - // std::cerr << "progress idle: " << progress->bar->get_text() << ". " << progress->dlnow << " / " << progress->dltotal << " = " << fraction << std::endl; + // std::cerr << "progress idle: " << progress_bar.get_text() << ". " << dlnow << " / " << dltotal << " = " << fraction << std::endl; if (fraction > 1.0) { fraction = 1.0; } else if (fraction < 0.0) { diff --git a/gtk2_ardour/sfdb_freesound_mootcher.h b/gtk2_ardour/sfdb_freesound_mootcher.h index e63f38ecbb..6d52436607 100644 --- a/gtk2_ardour/sfdb_freesound_mootcher.h +++ b/gtk2_ardour/sfdb_freesound_mootcher.h @@ -57,26 +57,26 @@ struct SfdbMemoryStruct { }; enum sortMethod { - sort_none, // no sort - sort_duration_desc, // Sort by the duration of the sounds, longest sounds first. - sort_duration_asc, // Same as above, but shortest sounds first. - sort_created_desc, // Sort by the date of when the sound was added. newest sounds first. - sort_created_asc, // Same as above, but oldest sounds first. - sort_downloads_desc, // Sort by the number of downloads, most downloaded sounds first. - sort_downloads_asc, // Same as above, but least downloaded sounds first. - sort_rating_desc, // Sort by the average rating given to the sounds, highest rated first. - sort_rating_asc // Same as above, but lowest rated sounds first. + sort_none, // no sort + sort_duration_descending, // Sort by the duration of the sounds, longest sounds first. + sort_duration_ascending, // Same as above, but shortest sounds first. + sort_created_descending, // Sort by the date of when the sound was added. newest sounds first. + sort_created_ascending, // Same as above, but oldest sounds first. + sort_downloads_descending, // Sort by the number of downloads, most downloaded sounds first. + sort_downloads_ascending, // Same as above, but least downloaded sounds first. + sort_rating_descending, // Sort by the average rating given to the sounds, highest rated first. + sort_rating_ascending // Same as above, but lowest rated sounds first. }; class Mootcher: public sigc::trackable, public PBD::ScopedConnectionList { public: - Mootcher(); + Mootcher(const std::string &token); ~Mootcher(); bool checkAudioFile(std::string originalFileName, std::string ID); - bool fetchAudioFile(std::string originalFileName, std::string ID, std::string audioURL, SoundFileBrowser *caller); + bool fetchAudioFile(std::string originalFileName, std::string ID, std::string audioURL, SoundFileBrowser *caller, std::string &token); std::string searchText(std::string query, int page, std::string filter, enum sortMethod sort); std::string searchSimilar(std::string id); void* threadFunc(); @@ -95,8 +95,11 @@ public: private: void ensureWorkingDir (); - std::string doRequest (std::string uri, std::string params); - void setcUrlOptions (); + bool get_oauth_token(); + std::string auth_code_to_oauth_token(const std::string &auth_code); + + std::string doRequest(std::string uri, std::string params); + void setcUrlOptions(); static size_t WriteMemoryCallback (void *ptr, size_t size, size_t nmemb, void *data); static int progress_callback (void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); @@ -110,6 +113,7 @@ private: void updateProgress(double dlnow, double dltotal); void doneWithMootcher(); + void report_login_error(const std::string &msg); Gtk::HBox progress_hbox; Gtk::ProgressBar progress_bar; @@ -123,6 +127,8 @@ private: std::string basePath; std::string xmlLocation; + std::string oauth_token; + struct curl_slist *custom_headers; }; #endif // __gtk_ardour_sfdb_freesound_mootcher_h__ diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index 649b809684..f56683de23 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -49,6 +49,9 @@ #include #include +#include "ardour/debug.h" + +#include "pbd/convert.h" #include "pbd/tokenizer.h" #include "pbd/enumwriter.h" #include "pbd/file_utils.h" @@ -712,7 +715,6 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s, bool persi VBox* vbox; HBox* hbox; - hbox = manage(new HBox); hbox->pack_start (found_entry); hbox->pack_start (found_search_btn); @@ -1063,25 +1065,33 @@ std::string SoundFileBrowser::freesound_get_audio_file(Gtk::TreeIter iter) { - Mootcher *mootcher = new Mootcher; + Mootcher *mootcher = new Mootcher(freesound_token); std::string file; string id = (*iter)[freesound_list_columns.id]; string uri = (*iter)[freesound_list_columns.uri]; - string ofn = (*iter)[freesound_list_columns.filename]; + string filename = (*iter)[freesound_list_columns.filename]; - if (mootcher->checkAudioFile(ofn, id)) { + if (mootcher->checkAudioFile(filename, id)) { // file already exists, no need to download it again file = mootcher->audioFileName; delete mootcher; - (*iter)[freesound_list_columns.started] = false; + (*iter)[freesound_list_columns.downloading] = false; return file; } - if (!(*iter)[freesound_list_columns.started]) { + + if (!(*iter)[freesound_list_columns.downloading]) { // start downloading the sound file - (*iter)[freesound_list_columns.started] = true; - mootcher->fetchAudioFile(ofn, id, uri, this); + DEBUG_TRACE(PBD::DEBUG::Freesound, string_compose("downloading %1 (id %2) from %3...\n", filename, id, uri)); + (*iter)[freesound_list_columns.downloading] = true; + // if we don't already have a token, fetchAudioFile() will get + // one: otherwse it'll return the one we already gave it. + if (!mootcher->fetchAudioFile(filename, id, uri, this, freesound_token)) { + // download cancelled or failed + (*iter)[freesound_list_columns.downloading] = false; + } } + return ""; } @@ -1101,7 +1111,7 @@ SoundFileBrowser::freesound_list_view_selected () switch (rows.size()) { case 0: // nothing selected - freesound_similar_btn.set_sensitive(false); + freesound_similar_btn.set_sensitive (false); set_action_sensitive (false); break; case 1: @@ -1111,13 +1121,17 @@ SoundFileBrowser::freesound_list_view_selected () chooser.set_filename (file); preview.setup_labels (file); set_action_sensitive (true); + } else { + // file doesn't exist yet, maybe still downloading: + // disable preview + preview.setup_labels (""); } - freesound_similar_btn.set_sensitive(true); + freesound_similar_btn.set_sensitive (true); break; default: // multiple items selected preview.setup_labels (""); - freesound_similar_btn.set_sensitive(false); + freesound_similar_btn.set_sensitive (false); break; } @@ -1136,7 +1150,7 @@ SoundFileBrowser::refresh_display(std::string ID, std::string file) std::string selected_ID = (*row)[freesound_list_columns.id]; if (ID == selected_ID) { // the selected item in the freesound list is the item that has just finished downloading - chooser.set_filename(file); + chooser.set_filename (file); preview.setup_labels (file); set_action_sensitive (true); } @@ -1167,7 +1181,7 @@ SoundFileBrowser::freesound_similar_clicked () { ListPath rows = freesound_list_view.get_selection()->get_selected_rows (); if (rows.size() == 1) { - Mootcher mootcher; + Mootcher mootcher(""); // no need for oauth token when searching string id; Gtk::TreeIter iter = freesound_list->get_iter(*rows.begin()); id = (*iter)[freesound_list_columns.id]; @@ -1188,7 +1202,7 @@ SoundFileBrowser::freesound_similar_clicked () void SoundFileBrowser::freesound_search() { - Mootcher mootcher; + Mootcher mootcher(""); // no need for oauth token when searching string search_string = freesound_entry.get_text (); enum sortMethod sort_method = (enum sortMethod) freesound_sort.get_active_row_number(); @@ -1204,7 +1218,7 @@ SoundFileBrowser::freesound_search() #ifdef __APPLE__ "", // OSX eats anything incl mp3 #else - "type:wav OR type:aiff OR type:flac OR type:aif OR type:ogg OR type:oga", + "type:(wav OR aiff OR flac OR aif OR ogg OR oga)", #endif sort_method ); @@ -1224,14 +1238,14 @@ SoundFileBrowser::handle_freesound_results(std::string theString) { return; } - if ( strcmp(root->name().c_str(), "response") != 0) { - error << string_compose ("root node name == %1 != \"response\"", root->name()) << endmsg; + if ( strcmp(root->name().c_str(), "root") != 0) { + error << string_compose ("root node name == %1 != \"root\"", root->name()) << endmsg; return; } // find out how many pages are available to search int freesound_n_pages = 1; - XMLNode *res = root->child("num_pages"); + XMLNode *res = root->child("count"); if (res) { string result = res->child("text")->content(); freesound_n_pages = atoi(result); @@ -1240,19 +1254,19 @@ SoundFileBrowser::handle_freesound_results(std::string theString) { int more_pages = freesound_n_pages - freesound_page; if (more_pages > 0) { - freesound_more_btn.set_sensitive(true); + freesound_more_btn.set_sensitive (true); freesound_more_btn.set_tooltip_text(string_compose(P_( "%1 more page of 100 results available", "%1 more pages of 100 results available", more_pages), more_pages)); } else { - freesound_more_btn.set_sensitive(false); + freesound_more_btn.set_sensitive (false); freesound_more_btn.set_tooltip_text(_("No more results available")); } - XMLNode *sounds_root = root->child("sounds"); + XMLNode *sounds_root = root->child("results"); if (!sounds_root) { - error << "no child node \"sounds\" found!" << endmsg; + error << "no child node \"results\" found!" << endmsg; return; } @@ -1266,36 +1280,43 @@ SoundFileBrowser::handle_freesound_results(std::string theString) { XMLNode *node; for (niter = sounds.begin(); niter != sounds.end(); ++niter) { node = *niter; - if( strcmp( node->name().c_str(), "resource") != 0 ) { - error << string_compose ("node->name()=%1 != \"resource\"", node->name()) << endmsg; + if( strcmp( node->name().c_str(), "list-item") != 0 ) { + error << string_compose ("node->name()=%1 != \"list-item\"", node->name()) << endmsg; break; } // node->dump(cerr, "node:"); - XMLNode *id_node = node->child ("id"); - XMLNode *uri_node = node->child ("serve"); - XMLNode *ofn_node = node->child ("original_filename"); - XMLNode *dur_node = node->child ("duration"); - XMLNode *siz_node = node->child ("filesize"); - XMLNode *srt_node = node->child ("samplerate"); - XMLNode *lic_node = node->child ("license"); + XMLNode *uri_node; + XMLNode *pre_node = node->child ("previews"); + if (false && pre_node) { + uri_node = pre_node->child ("preview-hq-ogg"); + } else { + uri_node = node->child ("download"); + } + XMLNode *filename_node = node->child ("name"); + XMLNode *duration_node = node->child ("duration"); + XMLNode *filesize_node = node->child ("filesize"); + XMLNode *samplerate_node = node->child ("samplerate"); + XMLNode *licence_node = node->child ("license"); - if (id_node && uri_node && ofn_node && dur_node && siz_node && srt_node) { + if (id_node && uri_node && filename_node && duration_node && filesize_node && samplerate_node) { - std::string id = id_node->child("text")->content(); - std::string uri = uri_node->child("text")->content(); - std::string ofn = ofn_node->child("text")->content(); - std::string dur = dur_node->child("text")->content(); - std::string siz = siz_node->child("text")->content(); - std::string srt = srt_node->child("text")->content(); - std::string lic = lic_node->child("text")->content(); + std::string id = id_node->child("text")->content(); + std::string uri = uri_node->child("text")->content(); + std::string filename = filename_node->child("text")->content(); + std::string duration = duration_node->child("text")->content(); + std::string filesize = filesize_node->child("text")->content(); + std::string samplerate = samplerate_node->child("text")->content(); + std::string licence = licence_node->child("text")->content(); - std::string r; - // cerr << "id=" << id << ",uri=" << uri << ",ofn=" << ofn << ",dur=" << dur << endl; + DEBUG_TRACE(PBD::DEBUG::Freesound, string_compose( + "id=%1 ,uri=%2 ,filename=%3 ,duration=%4\n", + id, uri, filename, duration + )); - double duration_seconds = atof(dur); + double duration_seconds = atof (duration); double h, m, s; char duration_hhmmss[16]; if (duration_seconds > 99 * 60 * 60) { @@ -1308,7 +1329,7 @@ SoundFileBrowser::handle_freesound_results(std::string theString) { ); } - double size_bytes = atof(siz); + double size_bytes = atof(filesize); char bsize[32]; if (size_bytes < 1000) { sprintf(bsize, "%.0f %s", size_bytes, _("B")); @@ -1324,16 +1345,16 @@ SoundFileBrowser::handle_freesound_results(std::string theString) { /* see http://www.freesound.org/help/faq/#licenses */ char shortlicense[64]; - if(!lic.compare(0, 42, "http://creativecommons.org/licenses/by-nc/")){ + if(!licence.compare(0, 42, "http://creativecommons.org/licenses/by-nc/")){ sprintf(shortlicense, "CC-BY-NC"); - } else if(!lic.compare(0, 39, "http://creativecommons.org/licenses/by/")) { + } else if(!licence.compare(0, 39, "http://creativecommons.org/licenses/by/")) { sprintf(shortlicense, "CC-BY"); - } else if(!lic.compare("http://creativecommons.org/licenses/sampling+/1.0/")) { + } else if(!licence.compare("http://creativecommons.org/licenses/sampling+/1.0/")) { sprintf(shortlicense, "sampling+"); - } else if(!lic.compare(0, 40, "http://creativecommons.org/publicdomain/")) { + } else if(!licence.compare(0, 40, "http://creativecommons.org/publicdomain/")) { sprintf(shortlicense, "PD"); } else { - snprintf(shortlicense, 64, "%s", lic.c_str()); + snprintf(shortlicense, 64, "%s", licence.c_str()); shortlicense[63]= '\0'; } @@ -1342,12 +1363,14 @@ SoundFileBrowser::handle_freesound_results(std::string theString) { row[freesound_list_columns.id ] = id; row[freesound_list_columns.uri ] = uri; - row[freesound_list_columns.filename] = ofn; + row[freesound_list_columns.filename] = filename; row[freesound_list_columns.duration] = duration_hhmmss; row[freesound_list_columns.filesize] = bsize; - row[freesound_list_columns.smplrate] = srt; + row[freesound_list_columns.smplrate] = samplerate; row[freesound_list_columns.license ] = shortlicense; matches++; + } else { + error << _("Failed to retrieve XML for file") << std::endl; } } } @@ -1510,8 +1533,6 @@ SoundFileOmega::reset_options () set_popdown_strings (action_combo, action_strings); /* preserve any existing choice, if possible */ - - if (existing_choice.length()) { vector::iterator x; for (x = action_strings.begin(); x != action_strings.end(); ++x) { diff --git a/gtk2_ardour/sfdb_ui.h b/gtk2_ardour/sfdb_ui.h index d57deb689b..978e6ae57d 100644 --- a/gtk2_ardour/sfdb_ui.h +++ b/gtk2_ardour/sfdb_ui.h @@ -157,7 +157,7 @@ private: Gtk::TreeModelColumn filesize; Gtk::TreeModelColumn smplrate; Gtk::TreeModelColumn license; - Gtk::TreeModelColumn started; + Gtk::TreeModelColumn downloading; FreesoundColumns() { add(id); @@ -167,7 +167,7 @@ private: add(filesize); add(smplrate); add(license); - add(started); + add(downloading); } }; @@ -251,6 +251,7 @@ protected: void freesound_more_clicked (); void freesound_similar_clicked (); int freesound_page; + std::string freesound_token; // keep oauth token while ardour is running void chooser_file_activated (); std::string freesound_get_audio_file(Gtk::TreeIter iter);