Working Soundcloud export
authorColin Fletcher <colin.m.fletcher@googlemail.com>
Mon, 7 Oct 2013 13:44:35 +0000 (14:44 +0100)
committerColin Fletcher <colin.m.fletcher@googlemail.com>
Mon, 7 Oct 2013 13:59:40 +0000 (14:59 +0100)
Adds an 'upload' property to ExportFormatSpecification, to indicate that files
exported with that format specfication should be uploaded to Soundcloud, and
makes it editable in the export format dialogue.
Adds fields for the Soundcloud username & password to the file format selection
page, as well as an option to make the uploaded files public and open them in
the system browser.

Possible improvements not yet implemented:
- make upload happen in its own thread
- cosmetic tidying up of dialogue control layout
- remember username & password

17 files changed:
gtk2_ardour/export_dialog.cc
gtk2_ardour/export_dialog.h
gtk2_ardour/export_format_dialog.cc
gtk2_ardour/export_format_dialog.h
gtk2_ardour/icons/soundcloud.png [new file with mode: 0644]
gtk2_ardour/soundcloud_export.cc [deleted file]
gtk2_ardour/soundcloud_export.h [deleted file]
gtk2_ardour/soundcloud_export_selector.cc [new file with mode: 0644]
gtk2_ardour/soundcloud_export_selector.h [new file with mode: 0644]
gtk2_ardour/wscript
libs/ardour/ardour/export_format_specification.h
libs/ardour/ardour/export_handler.h
libs/ardour/ardour/soundcloud_upload.h [new file with mode: 0644]
libs/ardour/export_format_specification.cc
libs/ardour/export_handler.cc
libs/ardour/soundcloud_upload.cc [new file with mode: 0644]
libs/ardour/wscript

index db3bbaf7968983de57804ce7289c46bd37509953..ae4e8b7f777a1313e86f74b4f41a6e802a9647c8 100644 (file)
@@ -151,6 +151,7 @@ ExportDialog::init_gui ()
        file_format_selector->set_homogeneous (false);
        file_format_selector->pack_start (*preset_align, false, false, 0);
        file_format_selector->pack_start (*file_notebook, false, false, 0);
+       file_format_selector->pack_start (*soundcloud_selector, false, false, 0);
 
        export_notebook.append_page (*file_format_selector, _("File format"));
        export_notebook.append_page (*timespan_selector, _("Time Span"));
@@ -168,6 +169,7 @@ ExportDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
        channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -257,10 +259,33 @@ ExportDialog::show_conflicting_files ()
        dialog.run();
 }
 
+void
+ExportDialog::soundcloud_upload_progress(double total, double now, std::string title)
+{
+       soundcloud_selector->do_progress_callback(total, now, title);
+
+}
+
 void
 ExportDialog::do_export ()
 {
        profile_manager->prepare_for_export ();
+       handler->upload_username = soundcloud_selector->username();
+       handler->upload_password = soundcloud_selector->password();
+       handler->upload_public   = soundcloud_selector->upload_public();
+       handler->upload_open     = soundcloud_selector->upload_open();
+
+       handler->SoundcloudProgress.connect_same_thread(
+                       *this, 
+                       boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3)
+       );
+#if 0
+       handler->SoundcloudProgress.connect(
+                       *this, invalidator (*this),
+                       boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3),
+                       gui_context()
+       );
+#endif
        handler->do_export ();
        show_progress ();
 }
@@ -370,6 +395,7 @@ ExportRangeDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, range_id));
        channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -383,6 +409,7 @@ ExportSelectionDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, X_("selection")));
        channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -407,6 +434,7 @@ ExportRegionDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, loc_id));
        channel_selector.reset (new RegionExportChannelSelector (_session, profile_manager, region, track));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -422,5 +450,6 @@ StemExportDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
        channel_selector.reset (new TrackExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
index 8e16d3837dfae71d24ef15eaba7d411feffc3c1f..0cc288f172abc4318b9949276e0bcc1a715bcfb5 100644 (file)
@@ -32,6 +32,7 @@
 #include "export_file_notebook.h"
 #include "export_preset_selector.h"
 #include "ardour_dialog.h"
+#include "soundcloud_export_selector.h"
 
 #include <gtkmm.h>
 
@@ -43,7 +44,8 @@ namespace ARDOUR {
 class ExportTimespanSelector;
 class ExportChannelSelector;
 
-class ExportDialog : public ArdourDialog {
+class ExportDialog : public ArdourDialog, public PBD::ScopedConnectionList 
+{
 
   public:
 
@@ -79,6 +81,7 @@ class ExportDialog : public ArdourDialog {
        boost::scoped_ptr<ExportTimespanSelector> timespan_selector;
        boost::scoped_ptr<ExportChannelSelector>  channel_selector;
        boost::scoped_ptr<ExportFileNotebook>     file_notebook;
+       boost::scoped_ptr<SoundcloudExportSelector> soundcloud_selector;
 
        Gtk::VBox                                 warning_widget;
        Gtk::VBox                                 progress_widget;
@@ -130,6 +133,8 @@ class ExportDialog : public ArdourDialog {
 
        float previous_progress; // Needed for gtk bug workaround
 
+       void soundcloud_upload_progress(double total, double now, std::string title);
+
        /* Buttons */
 
        Gtk::Button *           cancel_button;
index 1e8e87c7cf88e471be9a166c315df3ab186b355c..00a9466cb9f807ab28337ff187308b95c90ec6e1 100644 (file)
@@ -51,6 +51,8 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
   silence_end_checkbox (_("Add silence at end:")),
   silence_end_clock ("silence_end", true, "", true, false, true),
 
+  upload_checkbox(_("Upload to Soundcloud")),
+
   format_table (3, 4),
   compatibility_label (_("Compatibility"), Gtk::ALIGN_LEFT),
   quality_label (_("Quality"), Gtk::ALIGN_LEFT),
@@ -113,6 +115,7 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
        silence_table.attach (silence_end_checkbox, 1, 2, 2, 3);
        silence_table.attach (silence_end_clock, 2, 3, 2, 3);
 
+       get_vbox()->pack_start (upload_checkbox, false, false);
        /* Format table */
 
        init_format_table();
@@ -296,6 +299,7 @@ ExportFormatDialog::load_state (FormatPtr spec)
        }
 
        tag_checkbox.set_active (spec->tag());
+       upload_checkbox.set_active (spec->upload());
 }
 
 void
index 8d37ded2cdd5201c690cf73c82c8e277de398036..8e4b239ea0390ab512fcfe2629b10fb1a90ff5ac 100644 (file)
@@ -175,6 +175,10 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList
        Gtk::CheckButton silence_end_checkbox;
        AudioClock       silence_end_clock;
 
+       /* Upload */
+       
+       Gtk::CheckButton upload_checkbox;
+
        /* Format table */
 
        struct CompatibilityCols : public Gtk::TreeModelColumnRecord
diff --git a/gtk2_ardour/icons/soundcloud.png b/gtk2_ardour/icons/soundcloud.png
new file mode 100644 (file)
index 0000000..39c50fe
Binary files /dev/null and b/gtk2_ardour/icons/soundcloud.png differ
diff --git a/gtk2_ardour/soundcloud_export.cc b/gtk2_ardour/soundcloud_export.cc
deleted file mode 100644 (file)
index 90c03f7..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/* soundcloud_export.cpp **********************************************************************
-
-       Adapted for Ardour by Ben Loftis, March 2012
-
-       Licence GPL:
-
-       This program is free software; you can redistribute it and/or
-       modify it under the terms of the GNU General Public License
-       as published by the Free Software Foundation; either version 2
-       of the License, or (at your option) any later version.
-
-       This program is distributed in the hope that it will be useful,
-       but WITHOUT ANY WARRANTY; without even the implied warranty of
-       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-       GNU General Public License for more details.
-
-       You should have received a copy of the GNU General Public License
-       along with this program; if not, write to the Free Software
-       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
-
-*************************************************************************************/
-#include "soundcloud_export.h"
-
-#include "pbd/xml++.h"
-#include <pbd/error.h>
-//#include "pbd/filesystem.h"
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <iostream>
-#include <glib/gstdio.h>
-
-#include "i18n.h"
-
-using namespace PBD;
-
-static const std::string base_url = "http://api.soundcloud.com/tracks/13158665?client_id=";
-
-size_t
-WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
-{
-       register int realsize = (int)(size * nmemb);
-       struct MemoryStruct *mem = (struct MemoryStruct *)data;
-
-       mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
-
-       if (mem->memory) {
-               memcpy(&(mem->memory[mem->size]), ptr, realsize);
-               mem->size += realsize;
-               mem->memory[mem->size] = 0;
-       }
-       return realsize;
-}
-
-SoundcloudUploader::SoundcloudUploader()
-{
-       curl_handle = curl_easy_init();
-       multi_handle = curl_multi_init();
-}
-
-std::string
-SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
-{
-       struct MemoryStruct xml_page;
-       xml_page.memory = NULL;
-       xml_page.size = 0;
-
-       setcUrlOptions();
-
-       curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
-       curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
-
-       struct curl_httppost *formpost=NULL;
-       struct curl_httppost *lastptr=NULL;
-
-       /* Fill in the filename field */ 
-       curl_formadd(&formpost,
-                       &lastptr,
-                       CURLFORM_COPYNAME, "client_id",
-                       CURLFORM_COPYCONTENTS, "e7ac891eef866f139773cf8102b7a719",
-                       CURLFORM_END);
-
-       curl_formadd(&formpost,
-                       &lastptr,
-                       CURLFORM_COPYNAME, "client_secret",
-                       CURLFORM_COPYCONTENTS, "d78f34d19f09d26731801a0cb0f382c4",
-                       CURLFORM_END);
-
-       curl_formadd(&formpost,
-                       &lastptr,
-                       CURLFORM_COPYNAME, "grant_type",
-                       CURLFORM_COPYCONTENTS, "password",
-                       CURLFORM_END);
-
-       curl_formadd(&formpost,
-                       &lastptr,
-                       CURLFORM_COPYNAME, "username",
-                       CURLFORM_COPYCONTENTS, username.c_str(),
-                       CURLFORM_END);
-
-       curl_formadd(&formpost,
-                       &lastptr,
-                       CURLFORM_COPYNAME, "password",
-                       CURLFORM_COPYCONTENTS, password.c_str(),
-                       CURLFORM_END);
-
-       struct curl_slist *headerlist=NULL;
-       headerlist = curl_slist_append(headerlist, "Expect:");
-       headerlist = curl_slist_append(headerlist, "Accept: application/xml");
-       curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
-
-       /* what URL that receives this POST */ 
-       std::string url = "https://api.soundcloud.com/oauth2/token";
-       curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
-       curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
-
-       curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
-
-       // perform online request
-       CURLcode res = curl_easy_perform(curl_handle);
-       if( res != 0 ) {
-               std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl;
-               return "";
-       }
-
-       if(xml_page.memory){
-               //cheesy way to parse the json return value.  find access_token, then advance 3 quotes
-
-               if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
-                       error << _("Upload to Soundcloud failed.  Perhaps your email or password are incorrect?\n") << endmsg;
-                       return "";
-               }
-
-               std::string token = strtok( xml_page.memory, "access_token" );
-               token = strtok( NULL, "\"" );
-               token = strtok( NULL, "\"" );
-               token = strtok( NULL, "\"" );
-
-               free( xml_page.memory );
-               return token;
-       }
-
-       return "";
-}
-
-std::string
-SoundcloudUploader::Upload(std::string file_path, std::string title, std::string auth_token, bool ispublic, curl_progress_callback progress_callback, void *caller )
-{
-       int still_running;
-
-       struct MemoryStruct xml_page;
-       xml_page.memory = NULL;
-       xml_page.size = 0;
-
-       setcUrlOptions();
-
-       curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
-       curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
-
-       struct curl_httppost *formpost=NULL;
-       struct curl_httppost *lastptr=NULL;
-
-       /* Fill in the file upload field. This makes libcurl load data from
-          the given file name when curl_easy_perform() is called. */ 
-       curl_formadd(&formpost,
-                       &lastptr,
-                       CURLFORM_COPYNAME, "track[asset_data]",
-                       CURLFORM_FILE, file_path.c_str(),
-                       CURLFORM_END);
-
-       /* Fill in the filename field */ 
-       curl_formadd(&formpost,
-                       &lastptr,
-                       CURLFORM_COPYNAME, "oauth_token",
-                       CURLFORM_COPYCONTENTS, auth_token.c_str(),
-                       CURLFORM_END);
-
-       curl_formadd(&formpost,
-                       &lastptr,
-                       CURLFORM_COPYNAME, "track[title]",
-                       CURLFORM_COPYCONTENTS, title.c_str(),
-                       CURLFORM_END);
-
-       curl_formadd(&formpost,
-                       &lastptr,
-                       CURLFORM_COPYNAME, "track[sharing]",
-                       CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
-                       CURLFORM_END);
-
-       /* initalize custom header list (stating that Expect: 100-continue is not
-          wanted */ 
-       struct curl_slist *headerlist=NULL;
-       static const char buf[] = "Expect:";
-       headerlist = curl_slist_append(headerlist, buf);
-
-
-       if(curl_handle && multi_handle) {
-
-               /* what URL that receives this POST */ 
-               std::string url = "https://api.soundcloud.com/tracks";
-               curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
-               curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
-
-               curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
-               curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
-
-               curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
-               curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, progress_callback);
-               curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, caller);
-
-               curl_multi_add_handle(multi_handle, curl_handle);
-
-               curl_multi_perform(multi_handle, &still_running);
-
-
-               while(still_running) {
-                       struct timeval timeout;
-                       int rc; /* select() return code */ 
-
-                       fd_set fdread;
-                       fd_set fdwrite;
-                       fd_set fdexcep;
-                       int maxfd = -1;
-
-                       long curl_timeo = -1;
-
-                       FD_ZERO(&fdread);
-                       FD_ZERO(&fdwrite);
-                       FD_ZERO(&fdexcep);
-
-                       /* set a suitable timeout to play around with */ 
-                       timeout.tv_sec = 1;
-                       timeout.tv_usec = 0;
-
-                       curl_multi_timeout(multi_handle, &curl_timeo);
-                       if(curl_timeo >= 0) {
-                               timeout.tv_sec = curl_timeo / 1000;
-                               if(timeout.tv_sec > 1)
-                                       timeout.tv_sec = 1;
-                               else
-                                       timeout.tv_usec = (curl_timeo % 1000) * 1000;
-                       }
-
-                       /* get file descriptors from the transfers */ 
-                       curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
-
-                       /* In a real-world program you OF COURSE check the return code of the
-                          function calls.  On success, the value of maxfd is guaranteed to be
-                          greater or equal than -1.  We call select(maxfd + 1, ...), specially in
-                          case of (maxfd == -1), we call select(0, ...), which is basically equal
-                          to sleep. */ 
-
-                       rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
-
-                       switch(rc) {
-                               case -1:
-                                       /* select error */ 
-                                       break;
-                               case 0:
-                               default:
-                                       /* timeout or readable/writable sockets */ 
-                                       curl_multi_perform(multi_handle, &still_running);
-                                       break;
-                       }
-               } 
-
-               /* then cleanup the formpost chain */ 
-               curl_formfree(formpost);
-
-               /* free slist */ 
-               curl_slist_free_all (headerlist);
-       }
-
-       curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
-
-       if(xml_page.memory){
-
-               std::cout << xml_page.memory << std::endl;
-
-               XMLTree doc;
-               doc.read_buffer( xml_page.memory );
-               XMLNode *root = doc.root();
-
-               if (!root) {
-                       std::cout << "no root XML node!" << std::endl;
-                       return "";
-               }
-
-               XMLNode *url_node = root->child("permalink-url");
-               if (!url_node) {
-                       std::cout << "no child node \"permalink-url\" found!" << std::endl;
-                       return "";
-               }
-
-               XMLNode *text_node = url_node->child("text");
-               if (!text_node) {
-                       std::cout << "no text node found!" << std::endl;
-                       return "";
-               }
-
-               free( xml_page.memory );
-               return text_node->content();
-       }
-
-       return "";
-};
-
-
-SoundcloudUploader:: ~SoundcloudUploader()
-{
-       curl_easy_cleanup(curl_handle);
-       curl_multi_cleanup(multi_handle);
-}
-
-
-void
-SoundcloudUploader::setcUrlOptions()
-{
-       // basic init for curl
-       curl_global_init(CURL_GLOBAL_ALL);
-       // some servers don't like requests that are made without a user-agent field, so we provide one
-       curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
-       // setup curl error buffer
-       curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
-       // Allow redirection
-       curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
-       
-       // Allow connections to time out (without using signals)
-       curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
-       curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
-
-       curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
-       curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
-}
diff --git a/gtk2_ardour/soundcloud_export.h b/gtk2_ardour/soundcloud_export.h
deleted file mode 100644 (file)
index ff7fbd7..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*soundcloud_export.h****************************************************************************
-
-       Adapted for Ardour by Ben Loftis, March 2012
-
-*****************************************************************************/
-
-#include <string>
-#include <fstream>
-#include <iostream>
-#include <stdio.h>
-#include <cstring>
-#include <string>
-#include <sstream>
-#include <vector>
-#include <gtkmm/progressbar.h>
-//#include <ctime>
-
-#include "curl/curl.h"
-
-//--- struct to store XML file
-struct MemoryStruct {
-       char *memory;
-       size_t size;
-};
-
-
-class SoundcloudUploader
-{
-public:
-       SoundcloudUploader();
-       ~SoundcloudUploader();
-
-       std::string Get_Auth_Token( std::string username, std::string password );
-       std::string Upload( std::string file_path, std::string title, std::string auth_token, bool ispublic, curl_progress_callback, void* caller );
-
-private:
-
-       void            setcUrlOptions();
-
-       CURL *curl_handle;
-       CURLM *multi_handle;
-       char errorBuffer[CURL_ERROR_SIZE];      // storage for cUrl error message
-
-       std::string basePath;
-       std::string xmlLocation;
-};
-
diff --git a/gtk2_ardour/soundcloud_export_selector.cc b/gtk2_ardour/soundcloud_export_selector.cc
new file mode 100644 (file)
index 0000000..1ecab51
--- /dev/null
@@ -0,0 +1,110 @@
+/* soundcloud_export_selector.cpp ***************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+       Licence GPL:
+
+       This program is free software; you can redistribute it and/or
+       modify it under the terms of the GNU General Public License
+       as published by the Free Software Foundation; either version 2
+       of the License, or (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+
+*************************************************************************************/
+#include "ardour/soundcloud_upload.h"
+#include "soundcloud_export_selector.h"
+
+#include <pbd/error.h>
+#include "pbd/openuri.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+#include "ardour/session_metadata.h"
+#include "utils.h"
+
+SoundcloudExportSelector::SoundcloudExportSelector() :
+         sc_table (4, 3),
+         soundcloud_public_checkbox (_("Make file(s) public")),
+         soundcloud_username_label (_("User Email"), 1.0, 0.5),
+         soundcloud_password_label (_("Password"), 1.0, 0.5),
+         soundcloud_open_checkbox (_("Open uploaded files in browser")),
+         progress_bar()
+{
+
+
+       soundcloud_public_checkbox.set_name ("ExportCheckbox");
+       soundcloud_username_label.set_name ("ExportFormatLabel");
+       soundcloud_username_entry.set_name ("ExportFormatDisplay");
+       soundcloud_password_label.set_name ("ExportFormatLabel");
+       soundcloud_password_entry.set_name ("ExportFormatDisplay");
+
+       soundcloud_username_entry.set_text (ARDOUR::SessionMetadata::Metadata()->user_email());
+       soundcloud_password_entry.set_visibility(false);
+
+       Gtk::Frame *sc_frame = manage(new Gtk::Frame);
+       sc_frame->set_border_width(4);
+       sc_frame->set_shadow_type(Gtk::SHADOW_ETCHED_OUT);
+       sc_frame->set_name("soundcloud_export_box");
+       pack_start(*sc_frame, false, false);
+
+       sc_table.set_border_width(4);
+       sc_table.set_col_spacings (5);
+       sc_table.set_row_spacings (5);
+       sc_frame->add (sc_table);
+
+       //              sc_table.attach ( *( manage (new EventBox (::get_icon (X_("soundcloud"))))) , 0, 1,  0, 1);
+       sc_table.attach ( *(Gtk::manage (new Gtk::Image (get_icon (X_("soundcloud"))))) , 0, 1,  0, 2);
+
+       sc_table.attach (soundcloud_public_checkbox, 2, 3,  1, 2);
+       sc_table.attach (soundcloud_username_label, 0, 1,  3, 4);
+       sc_table.attach (soundcloud_username_entry, 1, 3,  3, 4);
+       sc_table.attach (soundcloud_password_label, 0, 1,  5, 6);
+       sc_table.attach (soundcloud_password_entry, 1, 3,  5, 6);
+       sc_table.attach (soundcloud_open_checkbox, 2, 3,  7, 8);
+
+       pack_end(progress_bar, false, false);
+       sc_frame->show_all();
+}
+
+
+int
+SoundcloudExportSelector::do_progress_callback(double ultotal, double ulnow, const std::string &filename)
+{
+       std::cerr << "SoundcloudExportSelector::do_progress_callback(" << ultotal << ", " << ulnow << ", " << filename << ")..." << std::endl; 
+       if (soundcloud_cancel) {
+               progress_bar.set_fraction (0);
+               // cancel_button.set_label ("");
+               return -1;
+       }
+
+       double fraction = 0.0;
+       if (ultotal != 0) {
+               fraction = ulnow / ultotal;
+       }
+
+       progress_bar.set_fraction ( fraction );
+
+       std::string prog;
+       prog = string_compose (_("%1: %2 of %3 bytes uploaded"), filename, ulnow, ultotal);
+       progress_bar.set_text( prog );
+
+
+       return 0;
+}
+
diff --git a/gtk2_ardour/soundcloud_export_selector.h b/gtk2_ardour/soundcloud_export_selector.h
new file mode 100644 (file)
index 0000000..7962ba8
--- /dev/null
@@ -0,0 +1,41 @@
+/*soundcloud_export_selector.h***********************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+*****************************************************************************/
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <gtkmm.h>
+#include <gtkmm/progressbar.h>
+
+class SoundcloudExportSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
+{
+  public:
+       SoundcloudExportSelector ();
+       int do_progress_callback (double ultotal, double ulnow, const std::string &filename);
+       std::string username () { return soundcloud_username_entry.get_text (); }
+       std::string password () { return soundcloud_password_entry.get_text (); }
+       bool upload_public () { return soundcloud_public_checkbox.get_active (); }
+       bool upload_open () { return soundcloud_open_checkbox.get_active (); }
+       void cancel () { soundcloud_cancel = true; }
+
+  private:
+       Gtk::Table  sc_table;
+       Gtk::CheckButton soundcloud_public_checkbox;
+       Gtk::Label soundcloud_username_label;
+       Gtk::Entry soundcloud_username_entry;
+       Gtk::Label soundcloud_password_label;
+       Gtk::Entry soundcloud_password_entry;
+       Gtk::CheckButton soundcloud_open_checkbox;
+       bool soundcloud_cancel;
+       Gtk::ProgressBar progress_bar;
+       
+};
+
index e6e5ba65af82e45ef4ef5a229c8f71cf5efc8950..9b6480278bfc01cac32f3cc745b6669e8d3ac344 100644 (file)
@@ -211,7 +211,7 @@ gtk2_ardour_sources = [
         'shuttle_control.cc',
         'simpleline.cc',
         'simplerect.cc',
-       'soundcloud_export.cc',
+       'soundcloud_export_selector.cc',
         'splash.cc',
         'speaker_dialog.cc',
         'startup.cc',
index fc03eb94b073ba2f729d2ee35025745d3222ebb9..cb99afdfa270c861b7c71f880762a638f3e8415b 100644 (file)
@@ -95,6 +95,7 @@ class ExportFormatSpecification : public ExportFormatBase {
        void set_tag (bool tag_it) { _tag = tag_it; }
        void set_with_cue (bool yn) { _with_cue = yn; }
        void set_with_toc (bool yn) { _with_toc = yn; }
+       void set_upload (bool yn) { _upload = yn; }
 
        void set_silence_beginning (AnyTime const & value) { _silence_beginning = value; }
        void set_silence_end (AnyTime const & value) { _silence_end = value; }
@@ -124,6 +125,7 @@ class ExportFormatSpecification : public ExportFormatBase {
        float normalize_target () const { return _normalize_target; }
        bool with_toc() const { return _with_toc; }
        bool with_cue() const { return _with_cue; }
+       bool upload() const { return _upload; }
 
        bool tag () const { return _tag && supports_tagging; }
 
@@ -173,6 +175,7 @@ class ExportFormatSpecification : public ExportFormatBase {
        float           _normalize_target;
        bool            _with_toc;
        bool            _with_cue;
+       bool            _upload;
 
        /* serialization helpers */
 
index d4dd5627f71e453e060766ec3963213ff201b2b9..7f667d2dee1ac37aa19851150881ff0a62b0352e 100644 (file)
@@ -30,6 +30,7 @@
 #include "ardour/export_pointers.h"
 #include "ardour/session.h"
 #include "ardour/types.h"
+#include "pbd/signals.h"
 
 namespace AudioGrapher {
        class BroadcastInfo;
@@ -67,7 +68,7 @@ class ExportElementFactory
        Session & session;
 };
 
-class ExportHandler : public ExportElementFactory
+class ExportHandler : public ExportElementFactory, public sigc::trackable
 {
   public:
        struct FileSpec {
@@ -104,6 +105,17 @@ class ExportHandler : public ExportElementFactory
 
        std::string get_cd_marker_filename(std::string filename, CDMarkerFormat format);
 
+       /** signal emitted when soundcloud export reports progress updates during upload.
+        * The parameters are total and current bytes downloaded, and the current filename
+        */
+       PBD::Signal3<void, double, double, std::string> SoundcloudProgress;
+
+       /* upload credentials & preferences */
+       std::string upload_username;
+       std::string upload_password;
+       bool upload_public;
+       bool upload_open;
+
   private:
 
        void handle_duplicate_format_extensions();
diff --git a/libs/ardour/ardour/soundcloud_upload.h b/libs/ardour/ardour/soundcloud_upload.h
new file mode 100644 (file)
index 0000000..6b8700e
--- /dev/null
@@ -0,0 +1,55 @@
+/* soundcloud_upload.h ******************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+*****************************************************************************/
+
+#ifndef __ardour_soundcloud_upload_h__
+#define __ardour_soundcloud_upload_h__
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "curl/curl.h"
+#include "ardour/session_handle.h"
+#include "ardour/export_handler.h"
+#include "pbd/signals.h"
+
+//--- struct to store XML file
+struct MemoryStruct {
+       char *memory;
+       size_t size;
+};
+
+
+class SoundcloudUploader
+{
+public:
+       SoundcloudUploader();
+       ~SoundcloudUploader();
+
+       std::string     Get_Auth_Token(std::string username, std::string password);
+       std::string Upload (std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller);
+       static int progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow);
+
+
+private:
+
+       void            setcUrlOptions();
+
+       CURL *curl_handle;
+       CURLM *multi_handle;
+       char errorBuffer[CURL_ERROR_SIZE];      // storage for cUrl error message
+
+       std::string title;
+       ARDOUR::ExportHandler *caller;
+
+};
+
+#endif /* __ardour_soundcloud_upload_h__ */
index b139faeee2997d8401a9e4de77dfe2547254ff74..588a156d4a10e6cb3d56f36d52b771e21ac8ba68 100644 (file)
@@ -170,6 +170,7 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s)
        , _normalize_target (1.0)
        , _with_toc (false)
        , _with_cue (false)
+       , _upload (false)
 {
        format_ids.insert (F_None);
        endiannesses.insert (E_FileDefault);
@@ -244,6 +245,7 @@ ExportFormatSpecification::get_state ()
        root->add_property ("id", _id.to_s());
        root->add_property ("with-cue", _with_cue ? "true" : "false");
        root->add_property ("with-toc", _with_toc ? "true" : "false");
+       root->add_property ("upload", _upload ? "true" : "false");
 
        node = root->add_child ("Encoding");
        node->add_property ("id", enum_2_string (format_id()));
@@ -321,6 +323,12 @@ ExportFormatSpecification::set_state (const XMLNode & root)
                _with_toc = false;
        }
        
+       if ((prop = root.property ("upload"))) {
+               _upload = string_is_affirmative (prop->value());
+       } else {
+               _upload = false;
+       }
+       
        /* Encoding and SRC */
 
        if ((child = root.child ("Encoding"))) {
@@ -590,6 +598,9 @@ ExportFormatSpecification::description (bool include_name)
                components.push_back ("CUE");
        }
 
+       if (_upload) {
+               components.push_back ("Upload");
+       }
        string desc;
        if (include_name) {
                desc = _name + ": ";
index 4a6b0552c5bfce4d98512dbe0274a0ebb32b089f..7ca6cb8c53dea1b28d94b02845a72b57d1ba98f0 100644 (file)
@@ -31,6 +31,9 @@
 #include "ardour/export_status.h"
 #include "ardour/export_format_specification.h"
 #include "ardour/export_filename.h"
+#include "ardour/soundcloud_upload.h"
+#include "pbd/openuri.h"
+#include "pbd/basename.h"
 
 #include "i18n.h"
 
@@ -280,22 +283,50 @@ ExportHandler::finish_timespan ()
        while (config_map.begin() != timespan_bounds.second) {
 
                ExportFormatSpecPtr fmt = config_map.begin()->second.format;
+               std::string filepath = config_map.begin()->second.filename->get_path(fmt);
 
                if (fmt->with_cue()) {
-                       export_cd_marker_file (current_timespan, fmt, config_map.begin()->second.filename->get_path(fmt), CDMarkerCUE);
+                       export_cd_marker_file (current_timespan, fmt, filepath, CDMarkerCUE);
                } 
 
                if (fmt->with_toc()) {
-                       export_cd_marker_file (current_timespan, fmt, config_map.begin()->second.filename->get_path(fmt), CDMarkerTOC);
+                       export_cd_marker_file (current_timespan, fmt, filepath, CDMarkerTOC);
                }
 
+               if (fmt->upload()) {
+                       SoundcloudUploader *soundcloud_uploader = new SoundcloudUploader;
+                       std::string token = soundcloud_uploader->Get_Auth_Token(upload_username, upload_password);
+                       std::cerr
+                               << "uploading "
+                               << filepath << std::endl
+                               << "username = " << upload_username
+                               << ", password = " << upload_password
+                               << " - token = " << token << " ..."
+                               << std::endl;
+                       std::string path = soundcloud_uploader->Upload (
+                                       filepath,
+                                       PBD::basename_nosuffix(filepath), // title
+                                       token,
+                                       upload_public,
+                                       this);
+
+                       if (path.length() != 0) {
+                               if (upload_open) {
+                               std::cerr << "opening " << path << " ..." << std::endl;
+                               open_uri(path.c_str());  // open the soundcloud website to the new file
+                               }
+                       } else {
+                               error << _("upload to Soundcloud failed.  Perhaps your email or password are incorrect?\n") << endmsg;
+                       }
+                       delete soundcloud_uploader;
+               }
                config_map.erase (config_map.begin());
        }
 
        start_timespan ();
 }
 
-/*** CD Marker sutff ***/
+/*** CD Marker stuff ***/
 
 struct LocationSortByStart {
     bool operator() (Location *a, Location *b) {
diff --git a/libs/ardour/soundcloud_upload.cc b/libs/ardour/soundcloud_upload.cc
new file mode 100644 (file)
index 0000000..f003d5a
--- /dev/null
@@ -0,0 +1,349 @@
+/* soundcloud_export.cpp **********************************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+       Licence GPL:
+
+       This program is free software; you can redistribute it and/or
+       modify it under the terms of the GNU General Public License
+       as published by the Free Software Foundation; either version 2
+       of the License, or (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to the Free Software
+       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+
+*************************************************************************************/
+#include "ardour/soundcloud_upload.h"
+
+#include "pbd/xml++.h"
+#include <pbd/error.h>
+//#include "pbd/filesystem.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+// static const std::string base_url = "http://api.soundcloud.com/tracks/13158665?client_id=";
+
+size_t
+WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+       register int realsize = (int)(size * nmemb);
+       struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+       mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+
+       if (mem->memory) {
+               memcpy(&(mem->memory[mem->size]), ptr, realsize);
+               mem->size += realsize;
+               mem->memory[mem->size] = 0;
+       }
+       return realsize;
+}
+
+SoundcloudUploader::SoundcloudUploader()
+{
+       curl_handle = curl_easy_init();
+       multi_handle = curl_multi_init();
+}
+
+std::string
+SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
+{
+       struct MemoryStruct xml_page;
+       xml_page.memory = NULL;
+       xml_page.size = 0;
+
+       setcUrlOptions();
+
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+       struct curl_httppost *formpost=NULL;
+       struct curl_httppost *lastptr=NULL;
+
+       /* Fill in the filename field */ 
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "client_id",
+                       CURLFORM_COPYCONTENTS, "e7ac891eef866f139773cf8102b7a719",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "client_secret",
+                       CURLFORM_COPYCONTENTS, "d78f34d19f09d26731801a0cb0f382c4",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "grant_type",
+                       CURLFORM_COPYCONTENTS, "password",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "username",
+                       CURLFORM_COPYCONTENTS, username.c_str(),
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "password",
+                       CURLFORM_COPYCONTENTS, password.c_str(),
+                       CURLFORM_END);
+
+       struct curl_slist *headerlist=NULL;
+       headerlist = curl_slist_append(headerlist, "Expect:");
+       headerlist = curl_slist_append(headerlist, "Accept: application/xml");
+       curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+
+       /* what URL that receives this POST */ 
+       std::string url = "https://api.soundcloud.com/oauth2/token";
+       curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+       curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+       // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+       // perform online request
+       CURLcode res = curl_easy_perform(curl_handle);
+       if( res != 0 ) {
+               std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl;
+               return "";
+       }
+
+       if(xml_page.memory){
+               //cheesy way to parse the json return value.  find access_token, then advance 3 quotes
+
+               if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
+                       error << _("Upload to Soundcloud failed.  Perhaps your email or password are incorrect?\n") << endmsg;
+                       return "";
+               }
+
+               std::string token = strtok( xml_page.memory, "access_token" );
+               token = strtok( NULL, "\"" );
+               token = strtok( NULL, "\"" );
+               token = strtok( NULL, "\"" );
+
+               free( xml_page.memory );
+               return token;
+       }
+
+       return "";
+}
+
+int
+SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
+{
+       SoundcloudUploader *scu = (SoundcloudUploader *) caller;
+       std::cerr << scu->title << ": uploaded " << ulnow << " of " << ultotal << std::endl;
+       scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
+       return 0;
+}
+
+
+std::string
+SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller)
+{
+       int still_running;
+
+       struct MemoryStruct xml_page;
+       xml_page.memory = NULL;
+       xml_page.size = 0;
+
+       setcUrlOptions();
+
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+       struct curl_httppost *formpost=NULL;
+       struct curl_httppost *lastptr=NULL;
+
+       /* Fill in the file upload field. This makes libcurl load data from
+          the given file name when curl_easy_perform() is called. */ 
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[asset_data]",
+                       CURLFORM_FILE, file_path.c_str(),
+                       CURLFORM_END);
+
+       /* Fill in the filename field */ 
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "oauth_token",
+                       CURLFORM_COPYCONTENTS, token.c_str(),
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[title]",
+                       CURLFORM_COPYCONTENTS, title.c_str(),
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[sharing]",
+                       CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
+                       CURLFORM_END);
+
+       /* initalize custom header list (stating that Expect: 100-continue is not
+          wanted */ 
+       struct curl_slist *headerlist=NULL;
+       static const char buf[] = "Expect:";
+       headerlist = curl_slist_append(headerlist, buf);
+
+
+       if (curl_handle && multi_handle) {
+
+               /* what URL that receives this POST */ 
+               std::string url = "https://api.soundcloud.com/tracks";
+               curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+               // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+               curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+               curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+               this->title = title; // save title to show in progress bar
+               this->caller = caller;
+
+               curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
+               curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
+               curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
+
+               curl_multi_add_handle(multi_handle, curl_handle);
+
+               curl_multi_perform(multi_handle, &still_running);
+
+
+               while(still_running) {
+                       struct timeval timeout;
+                       int rc; /* select() return code */ 
+
+                       fd_set fdread;
+                       fd_set fdwrite;
+                       fd_set fdexcep;
+                       int maxfd = -1;
+
+                       long curl_timeo = -1;
+
+                       FD_ZERO(&fdread);
+                       FD_ZERO(&fdwrite);
+                       FD_ZERO(&fdexcep);
+
+                       /* set a suitable timeout to play around with */ 
+                       timeout.tv_sec = 1;
+                       timeout.tv_usec = 0;
+
+                       curl_multi_timeout(multi_handle, &curl_timeo);
+                       if(curl_timeo >= 0) {
+                               timeout.tv_sec = curl_timeo / 1000;
+                               if(timeout.tv_sec > 1)
+                                       timeout.tv_sec = 1;
+                               else
+                                       timeout.tv_usec = (curl_timeo % 1000) * 1000;
+                       }
+
+                       /* get file descriptors from the transfers */ 
+                       curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
+
+                       /* In a real-world program you OF COURSE check the return code of the
+                          function calls.  On success, the value of maxfd is guaranteed to be
+                          greater or equal than -1.  We call select(maxfd + 1, ...), specially in
+                          case of (maxfd == -1), we call select(0, ...), which is basically equal
+                          to sleep. */ 
+
+                       rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+
+                       switch(rc) {
+                               case -1:
+                                       /* select error */ 
+                                       break;
+                               case 0:
+                               default:
+                                       /* timeout or readable/writable sockets */ 
+                                       curl_multi_perform(multi_handle, &still_running);
+                                       break;
+                       }
+               } 
+
+               /* then cleanup the formpost chain */ 
+               curl_formfree(formpost);
+
+               /* free slist */ 
+               curl_slist_free_all (headerlist);
+       }
+
+       curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
+
+       if(xml_page.memory){
+
+               std::cout << xml_page.memory << std::endl;
+
+               XMLTree doc;
+               doc.read_buffer( xml_page.memory );
+               XMLNode *root = doc.root();
+
+               if (!root) {
+                       std::cout << "no root XML node!" << std::endl;
+                       return "";
+               }
+
+               XMLNode *url_node = root->child("permalink-url");
+               if (!url_node) {
+                       std::cout << "no child node \"permalink-url\" found!" << std::endl;
+                       return "";
+               }
+
+               XMLNode *text_node = url_node->child("text");
+               if (!text_node) {
+                       std::cout << "no text node found!" << std::endl;
+                       return "";
+               }
+
+               free( xml_page.memory );
+               return text_node->content();
+       }
+
+       return "";
+};
+
+
+SoundcloudUploader:: ~SoundcloudUploader()
+{
+       curl_easy_cleanup(curl_handle);
+       curl_multi_cleanup(multi_handle);
+}
+
+
+void
+SoundcloudUploader::setcUrlOptions()
+{
+       // basic init for curl
+       curl_global_init(CURL_GLOBAL_ALL);
+       // some servers don't like requests that are made without a user-agent field, so we provide one
+       curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+       // setup curl error buffer
+       curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
+       // Allow redirection
+       curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
+       
+       // Allow connections to time out (without using signals)
+       curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+       curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
+
+       curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
+       curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
+}
+
index 0fd5c4e80f0dbbe2f310eb0fa66a4baa4c2eddd4..a2ed5891921d290942a3ba6fe125a03872b5a105 100644 (file)
@@ -197,6 +197,7 @@ libardour_sources = [
         'sndfile_helpers.cc',
         'sndfileimportable.cc',
         'sndfilesource.cc',
+        'soundcloud_upload.cc',
         'source.cc',
         'source_factory.cc',
         'speakers.cc',