From aced854ce7017de47eca618f0bf649ee8bdec56b Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sun, 16 Jun 2013 18:48:03 +0100 Subject: [PATCH 01/20] Remove unused files. --- gtk2_ardour/export_range_markers_dialog.cc | 209 --------------------- gtk2_ardour/export_range_markers_dialog.h | 66 ------- 2 files changed, 275 deletions(-) diff --git a/gtk2_ardour/export_range_markers_dialog.cc b/gtk2_ardour/export_range_markers_dialog.cc index 4b99162974..e69de29bb2 100644 --- a/gtk2_ardour/export_range_markers_dialog.cc +++ b/gtk2_ardour/export_range_markers_dialog.cc @@ -1,209 +0,0 @@ -/* - Copyright (C) 2006 Paul Davis - Author: Andre Raue - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include - -#include - -#include "ardour/audioengine.h" -#include "ardour/sndfile_helpers.h" - -#include "ardour_ui.h" -#include "export_range_markers_dialog.h" - -#include "i18n.h" - -using namespace Gtk; -using namespace ARDOUR; -using namespace PBD; -using namespace std; - -ExportRangeMarkersDialog::ExportRangeMarkersDialog (PublicEditor& editor) - : ExportDialog(editor) -{ - set_title (_("Export Ranges")); - file_frame.set_label (_("Export to Directory")); - - do_not_allow_export_cd_markers(); - - total_duration = 0; - current_range_marker_index = 0; -} - -Gtk::FileChooserAction -ExportRangeMarkersDialog::browse_action () const -{ - return Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER; -} - -void -ExportRangeMarkersDialog::export_data () -{ - getSession().locations()->apply(*this, &ExportRangeMarkersDialog::process_range_markers_export); -} - -void -ExportRangeMarkersDialog::process_range_markers_export(Locations::LocationList& locations) -{ - Locations::LocationList::iterator locationIter; - current_range_marker_index = 0; - init_progress_computing(locations); - - for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) { - Location *currentLocation = (*locationIter); - - if(currentLocation->is_range_marker()){ - // init filename - string filepath = get_target_filepath( - get_selected_file_name(), - currentLocation->name(), - get_selected_header_format()); - - initSpec(filepath); - - spec.start_frame = currentLocation->start(); - spec.end_frame = currentLocation->end(); - - if (getSession().start_export(spec)){ - // if export fails - return; - } - - // wait until export of this range finished - gtk_main_iteration(); - - while (spec.running){ - if(gtk_events_pending()){ - gtk_main_iteration(); - }else { - usleep(10000); - } - } - - current_range_marker_index++; - - getSession().stop_export (spec); - } - } - - spec.running = false; -} - - -string -ExportRangeMarkersDialog::get_target_filepath(string path, string filename, string postfix) -{ - string target_path = path; - if ((target_path.find_last_of ('/')) != string::npos) { - target_path += '/'; - } - - string target_filepath = target_path + filename + postfix; - struct stat statbuf; - - for(int counter=1; (stat (target_filepath.c_str(), &statbuf) == 0); counter++){ - // while file exists - ostringstream scounter; - scounter.flush(); - scounter << counter; - - target_filepath = - target_path + filename + "_" + scounter.str() + postfix; - } - - return target_filepath; -} - -bool -ExportRangeMarkersDialog::is_filepath_valid(string &filepath) -{ - // sanity check file name first - struct stat statbuf; - - if (filepath.empty()) { - // warning dialog - string txt = _("Please enter a valid target directory."); - MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true); - msg.run(); - return false; - } - - if ( (stat (filepath.c_str(), &statbuf) != 0) || - (!S_ISDIR (statbuf.st_mode)) ) { - string txt = _("Please select an existing target directory. Files are not allowed!"); - MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true); - msg.run(); - return false; - } - - // directory needs to exist and be writable - string dirpath = Glib::path_get_dirname (filepath); - if (!exists_and_writable (dirpath)) { - string txt = _("Cannot write file in: ") + dirpath; - MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true); - msg.run(); - return false; - } - - return true; -} - -void -ExportRangeMarkersDialog::init_progress_computing(Locations::LocationList& locations) -{ - // flush vector - range_markers_durations_aggregated.resize(0); - - framecnt_t duration_before_current_location = 0; - Locations::LocationList::iterator locationIter; - - for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) { - Location *currentLocation = (*locationIter); - - if(currentLocation->is_range_marker()){ - range_markers_durations_aggregated.push_back (duration_before_current_location); - - framecnt_t duration = currentLocation->end() - currentLocation->start(); - - range_markers_durations.push_back (duration); - duration_before_current_location += duration; - } - } - - total_duration = duration_before_current_location; -} - - -gint -ExportRangeMarkersDialog::progress_timeout () -{ - double progress = 0.0; - - if (current_range_marker_index >= range_markers_durations.size()){ - progress = 1.0; - } else{ - progress = ((double) range_markers_durations_aggregated[current_range_marker_index] + - (spec.progress * (double) range_markers_durations[current_range_marker_index])) / - (double) total_duration; - } - - set_progress_fraction( progress ); - return TRUE; -} diff --git a/gtk2_ardour/export_range_markers_dialog.h b/gtk2_ardour/export_range_markers_dialog.h index b0a29b5dc2..e69de29bb2 100644 --- a/gtk2_ardour/export_range_markers_dialog.h +++ b/gtk2_ardour/export_range_markers_dialog.h @@ -1,66 +0,0 @@ -/* - Copyright (C) 2006 Andre Raue - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifndef __export_range_markers_dialog_h__ -#define __export_range_markers_dialog_h__ - -#include "ardour/location.h" - -#include "export_dialog.h" - - -class ExportRangeMarkersDialog : public ExportDialog -{ - public: - ExportRangeMarkersDialog (PublicEditor&); - - Gtk::FileChooserAction browse_action() const; - - protected: - virtual bool is_filepath_valid(string &filepath); - - void export_data(); - - bool wants_dir() { return true; } - - private: - // keeps the duration of all range_markers before the current - vector range_markers_durations_aggregated; - vector range_markers_durations; - // duration of all range markers - nframes_t total_duration; - // index of range marker, that get's exported right now - unsigned int current_range_marker_index; - - // sets value of progress bar - virtual gint progress_timeout (); - - // initializes range_markers_durations_aggregated, range_markers_durations - // and total_duration - void init_progress_computing(ARDOUR::Locations::LocationList& locations); - - // searches for a filename like "." in path, that - // does not exist - string get_target_filepath(string path, string filename, string postfix); - - void process_range_markers_export(ARDOUR::Locations::LocationList&); -}; - - -#endif // __export_range_markers_dialog_h__ From 1d282f316f08c0704d1083df40a6871900a1fb3d Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sun, 16 Jun 2013 18:50:23 +0100 Subject: [PATCH 02/20] Show ranges in more sensible order in export dialog. Show ranges in order in which they appear on the timeline in export dialog, but always show the session range first. --- gtk2_ardour/export_timespan_selector.cc | 19 +++++++++++++++++++ gtk2_ardour/export_timespan_selector.h | 1 + 2 files changed, 20 insertions(+) diff --git a/gtk2_ardour/export_timespan_selector.cc b/gtk2_ardour/export_timespan_selector.cc index f239ab194b..53b7dc9fe0 100644 --- a/gtk2_ardour/export_timespan_selector.cc +++ b/gtk2_ardour/export_timespan_selector.cc @@ -105,6 +105,9 @@ ExportTimespanSelector::ExportTimespanSelector (ARDOUR::Session * session, Profi /* Range view */ range_list = Gtk::ListStore::create (range_cols); + // order by location start times + range_list->set_sort_column(range_cols.location, Gtk::SORT_ASCENDING); + range_list->set_sort_func(range_cols.location, sigc::mem_fun(*this, &ExportTimespanSelector::location_sorter)); range_view.set_model (range_list); range_view.set_headers_visible (true); } @@ -114,6 +117,22 @@ ExportTimespanSelector::~ExportTimespanSelector () } +int +ExportTimespanSelector::location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b) +{ + Location *l1 = (*a)[range_cols.location]; + Location *l2 = (*b)[range_cols.location]; + const Location *ls = _session->locations()->session_range_location(); + + // always sort session range first + if (l1 == ls) + return -1; + if (l2 == ls) + return +1; + + return l1->start() - l2->start(); +} + void ExportTimespanSelector::add_range_to_selection (ARDOUR::Location const * loc) { diff --git a/gtk2_ardour/export_timespan_selector.h b/gtk2_ardour/export_timespan_selector.h index 2118a57b83..31932c738b 100644 --- a/gtk2_ardour/export_timespan_selector.h +++ b/gtk2_ardour/export_timespan_selector.h @@ -84,6 +84,7 @@ class ExportTimespanSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr void update_range_name (std::string const & path, std::string const & new_text); void set_selection_state_of_all_timespans (bool); + int location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b); /*** GUI components ***/ From ebd00236f876125e2b548fded78aee0801f71844 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sun, 16 Jun 2013 18:47:16 +0100 Subject: [PATCH 03/20] Export dialogue visual re-arrangement Make the export dialogue into a tabbed notebook with three tabs: "File Format", "Timespan", and "Channels"/"Source". --- gtk2_ardour/export_channel_selector.cc | 8 +-- gtk2_ardour/export_dialog.cc | 68 +++++--------------------- gtk2_ardour/export_dialog.h | 15 +----- 3 files changed, 18 insertions(+), 73 deletions(-) diff --git a/gtk2_ardour/export_channel_selector.cc b/gtk2_ardour/export_channel_selector.cc index 10e3135b53..20155471af 100644 --- a/gtk2_ardour/export_channel_selector.cc +++ b/gtk2_ardour/export_channel_selector.cc @@ -458,15 +458,15 @@ RegionExportChannelSelector::RegionExportChannelSelector (ARDOUR::Session * _ses raw_button.set_label (string_compose (_("Region contents without fades nor region gain (channels: %1)"), region_chans)); raw_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection)); - vbox.pack_start (raw_button); + vbox.pack_start (raw_button, false, false); fades_button.set_label (string_compose (_("Region contents with fades and region gain (channels: %1)"), region_chans)); fades_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection)); - vbox.pack_start (fades_button); + vbox.pack_start (fades_button, false, false); processed_button.set_label (string_compose (_("Track output (channels: %1)"), track_chans)); processed_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection)); - vbox.pack_start (processed_button); + vbox.pack_start (processed_button, false, false); sync_with_manager(); vbox.show_all_children (); @@ -541,7 +541,7 @@ TrackExportChannelSelector::TrackExportChannelSelector (ARDOUR::Session * sessio // Options options_box.pack_start(region_contents_button); options_box.pack_start(track_output_button); - main_layout.pack_start(options_box); + main_layout.pack_start(options_box, false, false); // Track scroller track_scroller.add (track_view); diff --git a/gtk2_ardour/export_dialog.cc b/gtk2_ardour/export_dialog.cc index a64acd1680..db3bbaf796 100644 --- a/gtk2_ardour/export_dialog.cc +++ b/gtk2_ardour/export_dialog.cc @@ -140,69 +140,26 @@ ExportDialog::init () progress_widget.hide_all(); } -void -ExportDialog::expanded_changed () -{ - set_resizable(advanced->get_expanded()); -} - void ExportDialog::init_gui () { Gtk::Alignment * preset_align = Gtk::manage (new Gtk::Alignment()); preset_align->add (*preset_selector); preset_align->set_padding (0, 12, 0, 0); - get_vbox()->pack_start (*preset_align, false, false, 0); - Gtk::VPaned * advanced_paned = Gtk::manage (new Gtk::VPaned()); + Gtk::VBox * file_format_selector = Gtk::manage (new Gtk::VBox()); + 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); - Gtk::VBox* timespan_vbox = Gtk::manage (new Gtk::VBox()); - timespan_vbox->set_spacing (12); - timespan_vbox->set_border_width (12); + export_notebook.append_page (*file_format_selector, _("File format")); + export_notebook.append_page (*timespan_selector, _("Time Span")); + export_notebook.append_page (*channel_selector, _("Channels")); + + get_vbox()->pack_start (export_notebook, true, true, 0); + get_vbox()->pack_end (warning_widget, false, false, 0); + get_vbox()->pack_end (progress_widget, false, false, 0); - Gtk::Alignment * timespan_align = Gtk::manage (new Gtk::Alignment()); - timespan_label = Gtk::manage (new Gtk::Label (_("Time Span"), Gtk::ALIGN_LEFT)); - timespan_align->add (*timespan_selector); - timespan_align->set_padding (0, 0, 18, 0); - timespan_vbox->pack_start (*timespan_label, false, false, 0); - timespan_vbox->pack_start (*timespan_align, true, true, 0); - advanced_paned->pack1(*timespan_vbox, true, false); - - Gtk::VBox* channels_vbox = Gtk::manage (new Gtk::VBox()); - channels_vbox->set_spacing (12); - channels_vbox->set_border_width (12); - - Gtk::Alignment * channels_align = Gtk::manage (new Gtk::Alignment()); - channels_label = Gtk::manage (new Gtk::Label (_("Channels"), Gtk::ALIGN_LEFT)); - channels_align->add (*channel_selector); - channels_align->set_padding (0, 12, 18, 0); - channels_vbox->pack_start (*channels_label, false, false, 0); - channels_vbox->pack_start (*channels_align, true, true, 0); - advanced_paned->pack2(*channels_vbox, channel_selector_is_expandable(), false); - - get_vbox()->pack_start (*file_notebook, false, false, 0); - get_vbox()->pack_start (warning_widget, false, false, 0); - get_vbox()->pack_start (progress_widget, false, false, 0); - - advanced = Gtk::manage (new Gtk::Expander (_("Time span and channel options"))); - advanced->property_expanded().signal_changed().connect( - sigc::mem_fun(*this, &ExportDialog::expanded_changed)); - advanced->add (*advanced_paned); - - if (channel_selector_is_expandable()) { - advanced_sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_VERTICAL); - advanced_sizegroup->add_widget(*timespan_selector); - advanced_sizegroup->add_widget(*channel_selector); - } - - get_vbox()->pack_start (*advanced, true, true); - - Pango::AttrList bold; - Pango::Attribute b = Pango::Attribute::create_attr_weight (Pango::WEIGHT_BOLD); - bold.insert (b); - - timespan_label->set_attributes (bold); - channels_label->set_attributes (bold); } void @@ -439,8 +396,7 @@ void ExportRegionDialog::init_gui () { ExportDialog::init_gui (); - - channels_label->set_text (_("Source")); + export_notebook.set_tab_label_text(*export_notebook.get_nth_page(2), _("Source")); } void diff --git a/gtk2_ardour/export_dialog.h b/gtk2_ardour/export_dialog.h index 52c53660d8..8e16d3837d 100644 --- a/gtk2_ardour/export_dialog.h +++ b/gtk2_ardour/export_dialog.h @@ -75,9 +75,6 @@ class ExportDialog : public ArdourDialog { // Must initialize all the shared_ptrs below virtual void init_components (); - // Override if the channel selector should not be grown - virtual bool channel_selector_is_expandable() { return true; } - boost::scoped_ptr preset_selector; boost::scoped_ptr timespan_selector; boost::scoped_ptr channel_selector; @@ -86,15 +83,13 @@ class ExportDialog : public ArdourDialog { Gtk::VBox warning_widget; Gtk::VBox progress_widget; - Gtk::Label * timespan_label; - Gtk::Label * channels_label; + /*** GUI components ***/ + Gtk::Notebook export_notebook; private: void init (); - void expanded_changed(); - void notify_errors (); void close_dialog (); @@ -112,10 +107,7 @@ class ExportDialog : public ArdourDialog { PublicEditor & editor; StatusPtr status; - /*** GUI components ***/ - Glib::RefPtr advanced_sizegroup; - Gtk::Expander * advanced; /* Warning area */ @@ -170,9 +162,6 @@ class ExportRegionDialog : public ExportDialog public: ExportRegionDialog (PublicEditor & editor, ARDOUR::AudioRegion const & region, ARDOUR::AudioTrack & track); - protected: - virtual bool channel_selector_is_expandable() { return false; } - private: void init_gui (); void init_components (); From df8bbc556ed103308c0247ecfbbed3e0a1722179 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sun, 16 Jun 2013 18:48:33 +0100 Subject: [PATCH 04/20] Fix comment typos. --- gtk2_ardour/export_channel_selector.h | 2 +- gtk2_ardour/export_timespan_selector.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gtk2_ardour/export_channel_selector.h b/gtk2_ardour/export_channel_selector.h index 984026a748..734aa8e57b 100644 --- a/gtk2_ardour/export_channel_selector.h +++ b/gtk2_ardour/export_channel_selector.h @@ -122,7 +122,7 @@ class PortExportChannelSelector : public ExportChannelSelector typedef Gtk::TreeModelColumn > ComboCol; ComboCol port_list_col; - /* Channel struct, that represents the selected port and it's name */ + /* Channel struct, that represents the selected port and its name */ struct Channel { public: diff --git a/gtk2_ardour/export_timespan_selector.h b/gtk2_ardour/export_timespan_selector.h index 31932c738b..1c4a952913 100644 --- a/gtk2_ardour/export_timespan_selector.h +++ b/gtk2_ardour/export_timespan_selector.h @@ -128,7 +128,7 @@ class ExportTimespanSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr Gtk::ScrolledWindow range_scroller; }; -/// Allows seleting multiple timespans +/// Allows selecting multiple timespans class ExportTimespanSelectorMultiple : public ExportTimespanSelector { public: From ac362d9f4d0c4cb55ceb8da8f69da9cbb9f429b9 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 12 Aug 2013 14:50:37 +0100 Subject: [PATCH 05/20] soundcloud upload - initial copy of files from Mixbus. --- gtk2_ardour/soundcloud_export.cc | 334 +++++++++++++++++++++++++++++++ gtk2_ardour/soundcloud_export.h | 47 +++++ 2 files changed, 381 insertions(+) create mode 100644 gtk2_ardour/soundcloud_export.cc create mode 100644 gtk2_ardour/soundcloud_export.h diff --git a/gtk2_ardour/soundcloud_export.cc b/gtk2_ardour/soundcloud_export.cc new file mode 100644 index 0000000000..e97f6ab0cb --- /dev/null +++ b/gtk2_ardour/soundcloud_export.cc @@ -0,0 +1,334 @@ +/* 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 +//#include "pbd/filesystem.h" + +#include +#include +#include +#include + +#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(); +} + +string +SoundcloudUploader::Get_Auth_Token( string username, 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 ""; + } + + 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(string file_path, string title, 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 */ + 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 new file mode 100644 index 0000000000..2a5f51fbf4 --- /dev/null +++ b/gtk2_ardour/soundcloud_export.h @@ -0,0 +1,47 @@ +/*soundcloud_export.h**************************************************************************** + + Adapted for Ardour by Ben Loftis, March 2012 + +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#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; +}; + From 2c5c099fdd2dbf73e5e5b2f579de47c1a0b662c9 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Thu, 15 Aug 2013 17:12:59 +0100 Subject: [PATCH 06/20] soundcloud export: fix MS-DOS line endings. --- gtk2_ardour/soundcloud_export.cc | 668 +++++++++++++++---------------- gtk2_ardour/soundcloud_export.h | 94 ++--- 2 files changed, 381 insertions(+), 381 deletions(-) diff --git a/gtk2_ardour/soundcloud_export.cc b/gtk2_ardour/soundcloud_export.cc index e97f6ab0cb..47bd2d1090 100644 --- a/gtk2_ardour/soundcloud_export.cc +++ b/gtk2_ardour/soundcloud_export.cc @@ -1,334 +1,334 @@ -/* 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 -//#include "pbd/filesystem.h" - -#include -#include -#include -#include - -#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(); -} - -string -SoundcloudUploader::Get_Auth_Token( string username, 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 ""; - } - - 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(string file_path, string title, 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 */ - 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); -} +/* 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 +//#include "pbd/filesystem.h" + +#include +#include +#include +#include + +#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(); +} + +string +SoundcloudUploader::Get_Auth_Token( string username, 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 ""; + } + + 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(string file_path, string title, 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 */ + 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 index 2a5f51fbf4..ff7fbd7a6b 100644 --- a/gtk2_ardour/soundcloud_export.h +++ b/gtk2_ardour/soundcloud_export.h @@ -1,47 +1,47 @@ -/*soundcloud_export.h**************************************************************************** - - Adapted for Ardour by Ben Loftis, March 2012 - -*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include - -#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; -}; - +/*soundcloud_export.h**************************************************************************** + + Adapted for Ardour by Ben Loftis, March 2012 + +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#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; +}; + From 9591a480511da0749afdb51a744c97107604fe75 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Thu, 15 Aug 2013 17:14:18 +0100 Subject: [PATCH 07/20] soundcloud export: use tabs for indentation. --- gtk2_ardour/soundcloud_export.cc | 318 +++++++++++++++---------------- 1 file changed, 159 insertions(+), 159 deletions(-) diff --git a/gtk2_ardour/soundcloud_export.cc b/gtk2_ardour/soundcloud_export.cc index 47bd2d1090..54f83c9861 100644 --- a/gtk2_ardour/soundcloud_export.cc +++ b/gtk2_ardour/soundcloud_export.cc @@ -66,55 +66,55 @@ SoundcloudUploader::Get_Auth_Token( string username, string password ) 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_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); + curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist); - /* what URL that receives this POST */ + /* 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_URL, url.c_str()); + curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost); - curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); // perform online request CURLcode res = curl_easy_perform(curl_handle); @@ -130,7 +130,7 @@ SoundcloudUploader::Get_Auth_Token( string username, string password ) error << _("Upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg; return ""; } - + string token = strtok( xml_page.memory, "access_token" ); token = strtok( NULL, "\"" ); token = strtok( NULL, "\"" ); @@ -146,136 +146,136 @@ SoundcloudUploader::Get_Auth_Token( string username, string password ) std::string SoundcloudUploader::Upload(string file_path, string title, string auth_token, bool ispublic, curl_progress_callback progress_callback, void *caller ) { - int still_running; - - struct MemoryStruct xml_page; + 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 */ - 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); - + /* 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 */ + 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); - 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; + 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 ); @@ -291,13 +291,13 @@ std::cout << xml_page.memory << std::endl; 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(); } From 15b4ebbb074ce679c5ea7fb44061934fe572cad5 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Thu, 15 Aug 2013 17:20:49 +0100 Subject: [PATCH 08/20] add soundcloud export to wscript, and make it compile. add soundcloud exporter to gtk2_ardour/wscript, and fix compile errors by using std::string in place of string everywhere. --- gtk2_ardour/soundcloud_export.cc | 13 +++++++------ gtk2_ardour/wscript | 1 + 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/gtk2_ardour/soundcloud_export.cc b/gtk2_ardour/soundcloud_export.cc index 54f83c9861..90c03f720d 100644 --- a/gtk2_ardour/soundcloud_export.cc +++ b/gtk2_ardour/soundcloud_export.cc @@ -37,7 +37,8 @@ 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) +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; @@ -58,8 +59,8 @@ SoundcloudUploader::SoundcloudUploader() multi_handle = curl_multi_init(); } -string -SoundcloudUploader::Get_Auth_Token( string username, string password ) +std::string +SoundcloudUploader::Get_Auth_Token( std::string username, std::string password ) { struct MemoryStruct xml_page; xml_page.memory = NULL; @@ -131,7 +132,7 @@ SoundcloudUploader::Get_Auth_Token( string username, string password ) return ""; } - string token = strtok( xml_page.memory, "access_token" ); + std::string token = strtok( xml_page.memory, "access_token" ); token = strtok( NULL, "\"" ); token = strtok( NULL, "\"" ); token = strtok( NULL, "\"" ); @@ -144,7 +145,7 @@ SoundcloudUploader::Get_Auth_Token( string username, string password ) } std::string -SoundcloudUploader::Upload(string file_path, string title, string auth_token, bool ispublic, curl_progress_callback progress_callback, void *caller ) +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; @@ -197,7 +198,7 @@ SoundcloudUploader::Upload(string file_path, string title, string auth_token, bo if(curl_handle && multi_handle) { /* what URL that receives this POST */ - string url = "https://api.soundcloud.com/tracks"; + 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); diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index be7164cf05..e6e5ba65af 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -211,6 +211,7 @@ gtk2_ardour_sources = [ 'shuttle_control.cc', 'simpleline.cc', 'simplerect.cc', + 'soundcloud_export.cc', 'splash.cc', 'speaker_dialog.cc', 'startup.cc', From a3465ff5d3b830e79a0e383b3c85df0c8cc3af9e Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 7 Oct 2013 14:44:35 +0100 Subject: [PATCH 09/20] Working Soundcloud export 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 --- gtk2_ardour/export_dialog.cc | 29 +++++ gtk2_ardour/export_dialog.h | 7 +- gtk2_ardour/export_format_dialog.cc | 4 + gtk2_ardour/export_format_dialog.h | 4 + gtk2_ardour/icons/soundcloud.png | Bin 0 -> 1140 bytes gtk2_ardour/soundcloud_export.h | 47 -------- gtk2_ardour/soundcloud_export_selector.cc | 110 ++++++++++++++++++ gtk2_ardour/soundcloud_export_selector.h | 41 +++++++ gtk2_ardour/wscript | 2 +- .../ardour/export_format_specification.h | 3 + libs/ardour/ardour/export_handler.h | 14 ++- libs/ardour/ardour/soundcloud_upload.h | 55 +++++++++ libs/ardour/export_format_specification.cc | 11 ++ libs/ardour/export_handler.cc | 37 +++++- .../ardour/soundcloud_upload.cc | 32 +++-- libs/ardour/wscript | 1 + 16 files changed, 335 insertions(+), 62 deletions(-) create mode 100644 gtk2_ardour/icons/soundcloud.png delete mode 100644 gtk2_ardour/soundcloud_export.h create mode 100644 gtk2_ardour/soundcloud_export_selector.cc create mode 100644 gtk2_ardour/soundcloud_export_selector.h create mode 100644 libs/ardour/ardour/soundcloud_upload.h rename gtk2_ardour/soundcloud_export.cc => libs/ardour/soundcloud_upload.cc (89%) diff --git a/gtk2_ardour/export_dialog.cc b/gtk2_ardour/export_dialog.cc index db3bbaf796..ae4e8b7f77 100644 --- a/gtk2_ardour/export_dialog.cc +++ b/gtk2_ardour/export_dialog.cc @@ -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 ()); } diff --git a/gtk2_ardour/export_dialog.h b/gtk2_ardour/export_dialog.h index 8e16d3837d..0cc288f172 100644 --- a/gtk2_ardour/export_dialog.h +++ b/gtk2_ardour/export_dialog.h @@ -32,6 +32,7 @@ #include "export_file_notebook.h" #include "export_preset_selector.h" #include "ardour_dialog.h" +#include "soundcloud_export_selector.h" #include @@ -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 timespan_selector; boost::scoped_ptr channel_selector; boost::scoped_ptr file_notebook; + boost::scoped_ptr 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; diff --git a/gtk2_ardour/export_format_dialog.cc b/gtk2_ardour/export_format_dialog.cc index 1e8e87c7cf..00a9466cb9 100644 --- a/gtk2_ardour/export_format_dialog.cc +++ b/gtk2_ardour/export_format_dialog.cc @@ -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 diff --git a/gtk2_ardour/export_format_dialog.h b/gtk2_ardour/export_format_dialog.h index 8d37ded2cd..8e4b239ea0 100644 --- a/gtk2_ardour/export_format_dialog.h +++ b/gtk2_ardour/export_format_dialog.h @@ -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 index 0000000000000000000000000000000000000000..39c50fe7b32838351d9c2b7a4b94d83d58d73e87 GIT binary patch literal 1140 zcmV-)1dIELP)bSK6vg zTZ14ZC5?e-iI^Hv5K9Qg2jT;vJV;`Qf(gb%Kwq>14MJj6=!>SxE4;u14^RxGvOys{ z5Nc{Ekw$1Ev56s8q-9Iz&b`Mo+v#?8I=kIvx2=S^$=$OznS1a5ch0#VGe|`6C>IO? zLVyq;1PB2_fDm9V0p@U?f<5OrA7@cCW)V-b0zhr+b5s7|IrNFn76I~%Pt%M$1;8k} z+tbBk|DM%VHD%^1L&Jp{!ctZ^#Pjz5`@oJ@eH#DWea;b1PT%Pu3R(knXx(JTU7eV1 z%;RsrU~YV|`g;wrnVZqz6o5;tcUCw*351DTKb=1Y@91)|gr|2zwsd_A_jC zL(1hQsW!It+-8q)75UXtP=~zHWWmXrdM2P4`Rw6CyI)$?{a4~Phx1vI6a#iSD0UKW zFCfX;M<(0O36GfX!BKnnqZMnP{Oam?ucNW`GG;n2jA_KDJm34yvWlm4{B>nb>;n*T zT(I&1sKNl8QcBdwCZ6HkKtF^LBtE{W)@(X)(^5O@J2@|R{`kkpt&xOD@{OLkPZ_x5LkFoL|jrl#cVop+z#j>2w2TP@n~T--Tc$`z+3EKa1uNn?o4Vc3{s94wVLY6HBzC&kusmB~d&BQ*F z3rOByC1y)xo*SqO5Pn(=0YZQfAOr{jLVyt9F$VZgfB^sn -#include -#include -#include -#include -#include -#include -#include -#include -//#include - -#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 index 0000000000..1ecab514ab --- /dev/null +++ b/gtk2_ardour/soundcloud_export_selector.cc @@ -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 +#include "pbd/openuri.h" + +#include +#include +#include +#include + +#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 index 0000000000..7962ba8b06 --- /dev/null +++ b/gtk2_ardour/soundcloud_export_selector.h @@ -0,0 +1,41 @@ +/*soundcloud_export_selector.h*********************************************** + + Adapted for Ardour by Ben Loftis, March 2012 + +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + +}; + diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index e6e5ba65af..9b6480278b 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -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', diff --git a/libs/ardour/ardour/export_format_specification.h b/libs/ardour/ardour/export_format_specification.h index fc03eb94b0..cb99afdfa2 100644 --- a/libs/ardour/ardour/export_format_specification.h +++ b/libs/ardour/ardour/export_format_specification.h @@ -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 */ diff --git a/libs/ardour/ardour/export_handler.h b/libs/ardour/ardour/export_handler.h index d4dd5627f7..7f667d2dee 100644 --- a/libs/ardour/ardour/export_handler.h +++ b/libs/ardour/ardour/export_handler.h @@ -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 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 index 0000000000..6b8700e784 --- /dev/null +++ b/libs/ardour/ardour/soundcloud_upload.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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__ */ diff --git a/libs/ardour/export_format_specification.cc b/libs/ardour/export_format_specification.cc index b139faeee2..588a156d4a 100644 --- a/libs/ardour/export_format_specification.cc +++ b/libs/ardour/export_format_specification.cc @@ -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 + ": "; diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc index 4a6b0552c5..7ca6cb8c53 100644 --- a/libs/ardour/export_handler.cc +++ b/libs/ardour/export_handler.cc @@ -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/gtk2_ardour/soundcloud_export.cc b/libs/ardour/soundcloud_upload.cc similarity index 89% rename from gtk2_ardour/soundcloud_export.cc rename to libs/ardour/soundcloud_upload.cc index 90c03f720d..f003d5ab65 100644 --- a/gtk2_ardour/soundcloud_export.cc +++ b/libs/ardour/soundcloud_upload.cc @@ -20,7 +20,7 @@ *************************************************************************************/ -#include "soundcloud_export.h" +#include "ardour/soundcloud_upload.h" #include "pbd/xml++.h" #include @@ -35,7 +35,7 @@ using namespace PBD; -static const std::string base_url = "http://api.soundcloud.com/tracks/13158665?client_id="; +// 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) @@ -115,7 +115,7 @@ SoundcloudUploader::Get_Auth_Token( std::string username, std::string password ) 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); + // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); // perform online request CURLcode res = curl_easy_perform(curl_handle); @@ -144,8 +144,18 @@ SoundcloudUploader::Get_Auth_Token( std::string username, std::string password ) 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 auth_token, bool ispublic, curl_progress_callback progress_callback, void *caller ) +SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller) { int still_running; @@ -173,7 +183,7 @@ SoundcloudUploader::Upload(std::string file_path, std::string title, std::string curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "oauth_token", - CURLFORM_COPYCONTENTS, auth_token.c_str(), + CURLFORM_COPYCONTENTS, token.c_str(), CURLFORM_END); curl_formadd(&formpost, @@ -195,19 +205,22 @@ SoundcloudUploader::Upload(std::string file_path, std::string title, std::string headerlist = curl_slist_append(headerlist, buf); - if(curl_handle && multi_handle) { + 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_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, progress_callback); - curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, caller); + 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); @@ -333,3 +346,4 @@ SoundcloudUploader::setcUrlOptions() curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0); curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0); } + diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 0fd5c4e80f..a2ed589192 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -197,6 +197,7 @@ libardour_sources = [ 'sndfile_helpers.cc', 'sndfileimportable.cc', 'sndfilesource.cc', + 'soundcloud_upload.cc', 'source.cc', 'source_factory.cc', 'speakers.cc', From 8b9a1fae6a580b526b3fc784581e6d0c8031b6db Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 7 Oct 2013 20:22:56 +0100 Subject: [PATCH 10/20] Update export format when 'upload to soundcloud' changes. --- gtk2_ardour/export_format_dialog.cc | 6 ++++++ gtk2_ardour/export_format_dialog.h | 1 + libs/ardour/ardour/export_format_manager.h | 1 + libs/ardour/export_format_manager.cc | 6 ++++++ 4 files changed, 14 insertions(+) diff --git a/gtk2_ardour/export_format_dialog.cc b/gtk2_ardour/export_format_dialog.cc index 00a9466cb9..62d98c5cd2 100644 --- a/gtk2_ardour/export_format_dialog.cc +++ b/gtk2_ardour/export_format_dialog.cc @@ -145,6 +145,7 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) : with_cue.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_cue)); with_toc.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_toc)); + upload_checkbox.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_upload)); cue_toc_vbox.pack_start (with_cue, false, false); cue_toc_vbox.pack_start (with_toc, false, false); @@ -722,6 +723,11 @@ ExportFormatDialog::update_with_toc () } void +ExportFormatDialog::update_upload () +{ + manager.select_upload (upload_checkbox.get_active()); +} + ExportFormatDialog::update_description() { std::string text = ": " + format->description(false); diff --git a/gtk2_ardour/export_format_dialog.h b/gtk2_ardour/export_format_dialog.h index 8e4b239ea0..96405c33dd 100644 --- a/gtk2_ardour/export_format_dialog.h +++ b/gtk2_ardour/export_format_dialog.h @@ -311,6 +311,7 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList void update_with_toc (); void update_with_cue (); + void update_upload (); Gtk::TreeView sample_format_view; Gtk::TreeView dither_type_view; diff --git a/libs/ardour/ardour/export_format_manager.h b/libs/ardour/ardour/export_format_manager.h index 2b5d0ad804..4b4e43ae9c 100644 --- a/libs/ardour/ardour/export_format_manager.h +++ b/libs/ardour/ardour/export_format_manager.h @@ -100,6 +100,7 @@ class ExportFormatManager : public PBD::ScopedConnectionList void select_with_cue (bool); void select_with_toc (bool); + void select_upload (bool); void select_src_quality (ExportFormatBase::SRCQuality value); void select_trim_beginning (bool value); void select_silence_beginning (AnyTime const & time); diff --git a/libs/ardour/export_format_manager.cc b/libs/ardour/export_format_manager.cc index 890623c114..c8bd0c0aa9 100644 --- a/libs/ardour/export_format_manager.cc +++ b/libs/ardour/export_format_manager.cc @@ -294,6 +294,12 @@ ExportFormatManager::select_with_toc (bool value) } void +ExportFormatManager::select_upload (bool value) +{ + current_selection->set_upload (value); + check_for_description_change (); +} + ExportFormatManager::select_trim_beginning (bool value) { current_selection->set_trim_beginning (value); From 2a93f7a25e63c796745c4a34ebc13239382ba2df Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 7 Oct 2013 20:28:13 +0100 Subject: [PATCH 11/20] Rudimentary post-processing of exported files. Export format contains a string to be passed to system() after expanding %1, %2, & %3 via string_compose() to the full path & filename, containing directory, and basename respectively. No error-checking or any niceties like that - real programmers will of course always type the command correctly, and know to watch Ardour's standard output for the results... --- gtk2_ardour/export_format_dialog.cc | 13 +++++++++++++ gtk2_ardour/export_format_dialog.h | 3 +++ libs/ardour/ardour/export_format_manager.h | 1 + libs/ardour/ardour/export_format_specification.h | 3 +++ libs/ardour/export_format_manager.cc | 8 ++++++++ libs/ardour/export_format_specification.cc | 13 +++++++++++++ libs/ardour/export_handler.cc | 10 ++++++++++ 7 files changed, 51 insertions(+) diff --git a/gtk2_ardour/export_format_dialog.cc b/gtk2_ardour/export_format_dialog.cc index 62d98c5cd2..a05a6e549a 100644 --- a/gtk2_ardour/export_format_dialog.cc +++ b/gtk2_ardour/export_format_dialog.cc @@ -52,6 +52,7 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) : silence_end_clock ("silence_end", true, "", true, false, true), upload_checkbox(_("Upload to Soundcloud")), + command_label(_("Command to run post-export (%1=full path & filename, %2=directory, %3=basename):")), format_table (3, 4), compatibility_label (_("Compatibility"), Gtk::ALIGN_LEFT), @@ -116,6 +117,9 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) : silence_table.attach (silence_end_clock, 2, 3, 2, 3); get_vbox()->pack_start (upload_checkbox, false, false); + get_vbox()->pack_start (command_label, false, false); + get_vbox()->pack_start (command_entry, false, false); + /* Format table */ init_format_table(); @@ -146,6 +150,7 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) : with_cue.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_cue)); with_toc.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_toc)); upload_checkbox.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_upload)); + command_entry.signal_changed().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_command)); cue_toc_vbox.pack_start (with_cue, false, false); cue_toc_vbox.pack_start (with_toc, false, false); @@ -301,6 +306,7 @@ ExportFormatDialog::load_state (FormatPtr spec) tag_checkbox.set_active (spec->tag()); upload_checkbox.set_active (spec->upload()); + command_entry.set_text (spec->command()); } void @@ -728,6 +734,13 @@ ExportFormatDialog::update_upload () manager.select_upload (upload_checkbox.get_active()); } +void +ExportFormatDialog::update_command () +{ + manager.set_command (command_entry.get_text()); +} + +void ExportFormatDialog::update_description() { std::string text = ": " + format->description(false); diff --git a/gtk2_ardour/export_format_dialog.h b/gtk2_ardour/export_format_dialog.h index 96405c33dd..42ed1a9886 100644 --- a/gtk2_ardour/export_format_dialog.h +++ b/gtk2_ardour/export_format_dialog.h @@ -178,6 +178,8 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList /* Upload */ Gtk::CheckButton upload_checkbox; + Gtk::Label command_label; + Gtk::Entry command_entry; /* Format table */ @@ -312,6 +314,7 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList void update_with_toc (); void update_with_cue (); void update_upload (); + void update_command (); Gtk::TreeView sample_format_view; Gtk::TreeView dither_type_view; diff --git a/libs/ardour/ardour/export_format_manager.h b/libs/ardour/ardour/export_format_manager.h index 4b4e43ae9c..9fce8282a1 100644 --- a/libs/ardour/ardour/export_format_manager.h +++ b/libs/ardour/ardour/export_format_manager.h @@ -101,6 +101,7 @@ class ExportFormatManager : public PBD::ScopedConnectionList void select_with_cue (bool); void select_with_toc (bool); void select_upload (bool); + void set_command (std::string); void select_src_quality (ExportFormatBase::SRCQuality value); void select_trim_beginning (bool value); void select_silence_beginning (AnyTime const & time); diff --git a/libs/ardour/ardour/export_format_specification.h b/libs/ardour/ardour/export_format_specification.h index cb99afdfa2..768fbb3bb3 100644 --- a/libs/ardour/ardour/export_format_specification.h +++ b/libs/ardour/ardour/export_format_specification.h @@ -96,6 +96,7 @@ class ExportFormatSpecification : public ExportFormatBase { 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_command (std::string command) { _command = command; } void set_silence_beginning (AnyTime const & value) { _silence_beginning = value; } void set_silence_end (AnyTime const & value) { _silence_end = value; } @@ -126,6 +127,7 @@ class ExportFormatSpecification : public ExportFormatBase { bool with_toc() const { return _with_toc; } bool with_cue() const { return _with_cue; } bool upload() const { return _upload; } + std::string command() const { return _command; } bool tag () const { return _tag && supports_tagging; } @@ -176,6 +178,7 @@ class ExportFormatSpecification : public ExportFormatBase { bool _with_toc; bool _with_cue; bool _upload; + std::string _command; /* serialization helpers */ diff --git a/libs/ardour/export_format_manager.cc b/libs/ardour/export_format_manager.cc index c8bd0c0aa9..3ee940ffb6 100644 --- a/libs/ardour/export_format_manager.cc +++ b/libs/ardour/export_format_manager.cc @@ -300,6 +300,14 @@ ExportFormatManager::select_upload (bool value) check_for_description_change (); } +void +ExportFormatManager::set_command (std::string command) +{ + current_selection->set_command (command); + check_for_description_change (); +} + +void ExportFormatManager::select_trim_beginning (bool value) { current_selection->set_trim_beginning (value); diff --git a/libs/ardour/export_format_specification.cc b/libs/ardour/export_format_specification.cc index 588a156d4a..8b921519f7 100644 --- a/libs/ardour/export_format_specification.cc +++ b/libs/ardour/export_format_specification.cc @@ -171,6 +171,7 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s) , _with_toc (false) , _with_cue (false) , _upload (false) + , _command ("") { format_ids.insert (F_None); endiannesses.insert (E_FileDefault); @@ -246,6 +247,7 @@ ExportFormatSpecification::get_state () 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"); + root->add_property ("command", _command); node = root->add_child ("Encoding"); node->add_property ("id", enum_2_string (format_id())); @@ -329,6 +331,12 @@ ExportFormatSpecification::set_state (const XMLNode & root) _upload = false; } + if ((prop = root.property ("command"))) { + _command = prop->value(); + } else { + _command = ""; + } + /* Encoding and SRC */ if ((child = root.child ("Encoding"))) { @@ -601,6 +609,11 @@ ExportFormatSpecification::description (bool include_name) if (_upload) { components.push_back ("Upload"); } + + if (!_command.empty()) { + components.push_back ("+"); + } + string desc; if (include_name) { desc = _name + ": "; diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc index 7ca6cb8c53..042edaf788 100644 --- a/libs/ardour/export_handler.cc +++ b/libs/ardour/export_handler.cc @@ -293,6 +293,16 @@ ExportHandler::finish_timespan () export_cd_marker_file (current_timespan, fmt, filepath, CDMarkerTOC); } + if (!fmt->command().empty()) { + std::string command = string_compose(fmt->command(), + filepath, + Glib::path_get_dirname(filepath), + PBD::basename_nosuffix(filepath) + ); + std::cerr << "running command: " << command << "..." << std::endl; + system(command.c_str()); + } + if (fmt->upload()) { SoundcloudUploader *soundcloud_uploader = new SoundcloudUploader; std::string token = soundcloud_uploader->Get_Auth_Token(upload_username, upload_password); From 070bb7a8abcd787958b3ea07e77043dd404d653c Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 7 Oct 2013 21:16:42 +0100 Subject: [PATCH 12/20] Move system_exec from gtk2_ardour/ to libs/pbd/ --- gtk2_ardour/ardour_ui.cc | 2 +- gtk2_ardour/transcode_ffmpeg.h | 2 +- gtk2_ardour/video_monitor.h | 2 +- gtk2_ardour/wscript | 1 - {gtk2_ardour => libs/pbd/pbd}/system_exec.h | 0 {gtk2_ardour => libs/pbd}/system_exec.cc | 2 +- libs/pbd/wscript | 1 + 7 files changed, 5 insertions(+), 5 deletions(-) rename {gtk2_ardour => libs/pbd/pbd}/system_exec.h (100%) rename {gtk2_ardour => libs/pbd}/system_exec.cc (99%) diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index e74b8efe98..a56dbac216 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -122,7 +122,7 @@ typedef uint64_t microseconds_t; #include "video_server_dialog.h" #include "add_video_dialog.h" #include "transcode_video_dialog.h" -#include "system_exec.h" +#include "pbd/system_exec.h" #include "i18n.h" diff --git a/gtk2_ardour/transcode_ffmpeg.h b/gtk2_ardour/transcode_ffmpeg.h index 61267ac853..e25a68da91 100644 --- a/gtk2_ardour/transcode_ffmpeg.h +++ b/gtk2_ardour/transcode_ffmpeg.h @@ -22,7 +22,7 @@ #include #include "ardour/types.h" -#include "system_exec.h" +#include "pbd/system_exec.h" /** @class TranscodeFfmpeg diff --git a/gtk2_ardour/video_monitor.h b/gtk2_ardour/video_monitor.h index 59264fa012..80273cf5e3 100644 --- a/gtk2_ardour/video_monitor.h +++ b/gtk2_ardour/video_monitor.h @@ -26,7 +26,7 @@ #include "ardour/types.h" #include "ardour/session.h" #include "ardour/session_handle.h" -#include "system_exec.h" +#include "pbd/system_exec.h" namespace ARDOUR { class Session; diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 9b6480278b..b2d9999276 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -246,7 +246,6 @@ gtk2_ardour_sources = [ 'add_video_dialog.cc', 'editor_videotimeline.cc', 'video_timeline.cc', - 'system_exec.cc', 'video_monitor.cc', 'transcode_ffmpeg.cc', 'transcode_video_dialog.cc', diff --git a/gtk2_ardour/system_exec.h b/libs/pbd/pbd/system_exec.h similarity index 100% rename from gtk2_ardour/system_exec.h rename to libs/pbd/pbd/system_exec.h diff --git a/gtk2_ardour/system_exec.cc b/libs/pbd/system_exec.cc similarity index 99% rename from gtk2_ardour/system_exec.cc rename to libs/pbd/system_exec.cc index 59f3647e51..d26eedad6d 100644 --- a/gtk2_ardour/system_exec.cc +++ b/libs/pbd/system_exec.cc @@ -40,7 +40,7 @@ #endif -#include "system_exec.h" +#include "pbd/system_exec.h" using namespace std; void * interposer_thread (void *arg); diff --git a/libs/pbd/wscript b/libs/pbd/wscript index ffbe0e0d19..65b6a43a2e 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -71,6 +71,7 @@ libpbd_sources = [ 'stateful.cc', 'strreplace.cc', 'strsplit.cc', + 'system_exec.cc', 'textreceiver.cc', 'transmitter.cc', 'undo.cc', From e1562961c0bf87daa98417f2a660fbf8ebd04a35 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Thu, 10 Oct 2013 19:50:21 +0100 Subject: [PATCH 13/20] Add command-line parsing constructor to SystemExec Add a new constructor to SystemExec which takes a complete command-line and a map of substitutions for %, and parses the command line, taking account of some simple escape sequences and quoting. --- libs/pbd/pbd/system_exec.h | 22 ++++++++ libs/pbd/system_exec.cc | 113 ++++++++++++++++++++++++++++++++----- 2 files changed, 121 insertions(+), 14 deletions(-) diff --git a/libs/pbd/pbd/system_exec.h b/libs/pbd/pbd/system_exec.h index cf8518f547..dc17ced2f6 100644 --- a/libs/pbd/pbd/system_exec.h +++ b/libs/pbd/pbd/system_exec.h @@ -33,6 +33,8 @@ #include #include #include +#include + #ifdef NOPBD /* outside ardour */ #include #include @@ -83,8 +85,26 @@ class SystemExec * */ SystemExec (std::string c, char ** a); + + /** similar to \ref SystemExec but expects a whole command line, and + * handles some simple escape sequences. + * + * @param command complete command-line to be executed + * @param subs a map of listing the % substitutions to + * be made. + * + * creates an argv array from the given command string, splitting into + * parameters at spaces. + * "\ " is non-splitting space, "\\" (and "\" at end of command) as "\", + * for "%", is looked up in subs and the corresponding string + * substituted. "%%" (and "%" at end of command) + * returns an argv array suitable for creating a new SystemExec with + */ + SystemExec (std::string command, const std::map subs); + virtual ~SystemExec (); + /** fork and execute the given program * * @param stderr_mode select what to do with program's standard error @@ -171,6 +191,7 @@ class SystemExec int nicelevel; ///< process nice level - defaults to 0 void make_argp(std::string); + void make_argp_escaped(std::string command, const std::map subs); void make_envp(); char **argp; @@ -187,6 +208,7 @@ class SystemExec #else pid_t pid; #endif + void init (); pthread_mutex_t write_lock; int fdin; ///< file-descriptor for writing to child's STDIN. This variable is identical to pin[1] but also used as status check if the stdin pipe is open: <0 means closed. diff --git a/libs/pbd/system_exec.cc b/libs/pbd/system_exec.cc index d26eedad6d..f270364660 100644 --- a/libs/pbd/system_exec.cc +++ b/libs/pbd/system_exec.cc @@ -144,9 +144,8 @@ static int close_allv(const int except_fds[]) { } #endif /* not on windows */ - -SystemExec::SystemExec (std::string c, std::string a) - : cmd(c) +void +SystemExec::init () { pthread_mutex_init(&write_lock, NULL); thread_active=false; @@ -154,12 +153,19 @@ SystemExec::SystemExec (std::string c, std::string a) pin[1] = -1; nicelevel = 0; envp = NULL; - argp = NULL; #ifdef __WIN32__ stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE; stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE; stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE; #endif +} + +SystemExec::SystemExec (std::string c, std::string a) + : cmd(c) +{ + init (); + + argp = NULL; make_envp(); make_argp(a); } @@ -167,21 +173,100 @@ SystemExec::SystemExec (std::string c, std::string a) SystemExec::SystemExec (std::string c, char **a) : cmd(c) , argp(a) { - pthread_mutex_init(&write_lock, NULL); - thread_active=false; - pid = 0; - pin[1] = -1; - nicelevel = 0; - envp = NULL; + init (); + #ifdef __WIN32__ - stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE; - stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE; - stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE; make_wargs(a); #endif make_envp(); } +SystemExec::SystemExec (std::string command, const std::map subs) +{ + init (); + make_argp_escaped(command, subs); + cmd = argp[0]; + // cmd = strdup(argp[0]); + make_envp(); +} + +void +SystemExec::make_argp_escaped(std::string command, const std::map subs) +{ + + int inquotes = 0; + int n = 0; + size_t i = 0; + std::string arg = ""; + + argp = (char **) malloc(sizeof(char *)); + + for (i = 0; i < command.length(); i++) { + char c = command.c_str()[i]; + if (inquotes) { + if (c == '"') { + inquotes = 0; + } else { + // still in quotes - just copy + arg += c; + } + } else switch (c) { + case '%' : + c = command.c_str()[++i]; + if (c == '%' || c == '\0') { + // "%%", "%" at end-of-string => "%" + arg += '%'; + } else { + // search subs for string to substitute for char + std::map::const_iterator s = subs.find(c); + if (s != subs.end()) { + // found substitution + arg += s->second; + } else { + // not a valid substitution, just copy + arg += '%'; + arg += c; + } + } + break; + case '\\': + c = command.c_str()[++i]; + switch (c) { + case ' ' : + case '"' : arg += c; break; // "\\", "\" at end-of-string => "\" + case '\0': + case '\\': arg += '\\'; break; + default : arg += '\\'; arg += c; break; + } + break; + case '"' : + inquotes = 1; + break; + case ' ' : + case '\t': + if (arg.length() > 0) { + // if there wasn't already a space or tab, start a new parameter + argp = (char **) realloc(argp, (n + 2) * sizeof(char *)); + argp[n++] = strdup (arg.c_str()); + arg = ""; + } + break; + default : + arg += c; + break; + } + } + argp[n] = NULL; + + char *p = argp[0]; + n = 0; + do { + std::cerr << "argv[" << n << "] == \"" << p << "\"" << std::endl; + p = argp[n++]; + } while (p); + +} + SystemExec::~SystemExec () { terminate (); @@ -502,7 +587,7 @@ SystemExec::make_argp(std::string args) { *cp2 = '\0'; argp[argn++] = strdup(cp1); cp1 = cp2 + 1; - argp = (char **) realloc(argp, (argn + 1) * sizeof(char *)); + argp = (char **) realloc(argp, (argn + 1) * sizeof(char *)); } } if (cp2 != cp1) { From cac644270a8626904a4a21f527cdcf5083defa60 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Thu, 10 Oct 2013 19:54:22 +0100 Subject: [PATCH 14/20] Use SystemExec for post-export hook Use the new command-line parsing constructor for SystemExec to construct the args array for the post-export hook from the entered command string, with some simple substitutions for filename, directory, &c. --- gtk2_ardour/export_format_dialog.cc | 2 +- libs/ardour/ardour/export_handler.h | 2 ++ libs/ardour/export_handler.cc | 49 ++++++++++++++++++++++++----- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/gtk2_ardour/export_format_dialog.cc b/gtk2_ardour/export_format_dialog.cc index a05a6e549a..0e2da53cfd 100644 --- a/gtk2_ardour/export_format_dialog.cc +++ b/gtk2_ardour/export_format_dialog.cc @@ -52,7 +52,7 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) : silence_end_clock ("silence_end", true, "", true, false, true), upload_checkbox(_("Upload to Soundcloud")), - command_label(_("Command to run post-export (%1=full path & filename, %2=directory, %3=basename):")), + command_label(_("Command to run post-export\n(%f=full path & filename, %d=directory, %b=basename, %u=username, %p=password):")), format_table (3, 4), compatibility_label (_("Compatibility"), Gtk::ALIGN_LEFT), diff --git a/libs/ardour/ardour/export_handler.h b/libs/ardour/ardour/export_handler.h index 7f667d2dee..e2c0a7b2b7 100644 --- a/libs/ardour/ardour/export_handler.h +++ b/libs/ardour/ardour/export_handler.h @@ -95,6 +95,8 @@ class ExportHandler : public ExportElementFactory, public sigc::trackable friend boost::shared_ptr Session::get_export_handler(); ExportHandler (Session & session); + void command_output(std::string output, size_t size); + public: ~ExportHandler (); diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc index 042edaf788..f2fb895b91 100644 --- a/libs/ardour/export_handler.cc +++ b/libs/ardour/export_handler.cc @@ -34,6 +34,7 @@ #include "ardour/soundcloud_upload.h" #include "pbd/openuri.h" #include "pbd/basename.h" +#include "pbd/system_exec.h" #include "i18n.h" @@ -277,6 +278,13 @@ ExportHandler::process_normalize () return 0; } +void +ExportHandler::command_output(std::string output, size_t size) +{ + std::cerr << "command: " << size << ", " << output << std::endl; + info << output << endmsg; +} + void ExportHandler::finish_timespan () { @@ -294,13 +302,40 @@ ExportHandler::finish_timespan () } if (!fmt->command().empty()) { - std::string command = string_compose(fmt->command(), - filepath, - Glib::path_get_dirname(filepath), - PBD::basename_nosuffix(filepath) - ); - std::cerr << "running command: " << command << "..." << std::endl; - system(command.c_str()); + +#if 0 // would be nicer with C++11 initialiser... + std::map subs { + { 'f', filepath }, + { 'd', Glib::path_get_dirname(filepath) }, + { 'b', PBD::basename_nosuffix(filepath) }, + { 'u', upload_username }, + { 'p', upload_password} + }; +#endif + + PBD::ScopedConnection command_connection; + std::map subs; + subs.insert (std::pair ('f', filepath)); + subs.insert (std::pair ('d', Glib::path_get_dirname(filepath))); + subs.insert (std::pair ('b', PBD::basename_nosuffix(filepath))); + subs.insert (std::pair ('u', upload_username)); + subs.insert (std::pair ('p', upload_password)); + + + std::cerr << "running command: " << fmt->command() << "..." << std::endl; + SystemExec *se = new SystemExec(fmt->command(), subs); + se->ReadStdout.connect_same_thread(command_connection, boost::bind(&ExportHandler::command_output, this, _1, _2)); + if (se->start (2) == 0) { + // successfully started + std::cerr << "started!" << std::endl; + while (se->is_running ()) { + // wait for system exec to terminate + // std::cerr << "waiting..." << std::endl; + usleep (1000); + } + } + std::cerr << "done! deleting..." << std::endl; + delete (se); } if (fmt->upload()) { From d23135f36bd35066f1ed6769ede2002e33a1bf66 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sat, 11 Jan 2014 00:15:09 +0000 Subject: [PATCH 15/20] Soundcloud upload example export format presets. Add a couple of example export format profiles with Soundcloud upload enabled, so that people can upload to Soundcoud without having to go into the 'Edit Export Format Profile' dialogue and enable them there. There's probably still a discussion to be had about exactly what formats these profiles should have, and also their names, but I think they're a reasonably sensible starting point. --- export/Soundcloud upload (lossless).format | 30 ++++++++++++++++++++++ export/Soundcloud upload (lossy).format | 30 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 export/Soundcloud upload (lossless).format create mode 100644 export/Soundcloud upload (lossy).format diff --git a/export/Soundcloud upload (lossless).format b/export/Soundcloud upload (lossless).format new file mode 100644 index 0000000000..eb966af86b --- /dev/null +++ b/export/Soundcloud upload (lossless).format @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/export/Soundcloud upload (lossy).format b/export/Soundcloud upload (lossy).format new file mode 100644 index 0000000000..d948b1d97d --- /dev/null +++ b/export/Soundcloud upload (lossy).format @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + From 7c4259133d4ca965f7aed5cbb419d75125de6bef Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sun, 18 May 2014 20:49:58 +0100 Subject: [PATCH 16/20] SystemExec: copy last argument from command line to argv[] Make sure to copy any part of the command line after the last delimiter to argv[] when creating a SystemExec with an escaped command line. --- libs/pbd/system_exec.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/pbd/system_exec.cc b/libs/pbd/system_exec.cc index f270364660..bf02fd7254 100644 --- a/libs/pbd/system_exec.cc +++ b/libs/pbd/system_exec.cc @@ -201,7 +201,7 @@ SystemExec::make_argp_escaped(std::string command, const std::map 0) { // if there wasn't already a space or tab, start a new parameter argp = (char **) realloc(argp, (n + 2) * sizeof(char *)); From ba08ae96419d0605eb05921a7428173f48f0fbe0 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sun, 18 May 2014 22:33:25 +0100 Subject: [PATCH 17/20] Use ARDOUR namespace for SystemExec Explicitly use ARDOUR::SystemExec, and #include the right header for it too. --- libs/ardour/export_handler.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc index 01f5c989d3..4bf3571f53 100644 --- a/libs/ardour/export_handler.cc +++ b/libs/ardour/export_handler.cc @@ -33,9 +33,9 @@ #include "ardour/export_format_specification.h" #include "ardour/export_filename.h" #include "ardour/soundcloud_upload.h" +#include "ardour/system_exec.h" #include "pbd/openuri.h" #include "pbd/basename.h" -#include "pbd/system_exec.h" #include "ardour/session_metadata.h" #include "i18n.h" @@ -329,7 +329,7 @@ ExportHandler::finish_timespan () std::cerr << "running command: " << fmt->command() << "..." << std::endl; - SystemExec *se = new SystemExec(fmt->command(), subs); + ARDOUR::SystemExec *se = new ARDOUR::SystemExec(fmt->command(), subs); se->ReadStdout.connect_same_thread(command_connection, boost::bind(&ExportHandler::command_output, this, _1, _2)); if (se->start (2) == 0) { // successfully started From 65dc32f9ec083c763761c79fb9306032e32e89b5 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sun, 18 May 2014 23:02:24 +0100 Subject: [PATCH 18/20] Add back libardour wrappers for PBD::SystemExec Add back ARDOUR::SystemExec wrapper for PBD::SystemExec, and add constructor for command line with parameter substitution. --- libs/ardour/ardour/system_exec.h | 51 +++++++++++++++++++++ libs/ardour/system_exec.cc | 78 ++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 libs/ardour/ardour/system_exec.h create mode 100644 libs/ardour/system_exec.cc diff --git a/libs/ardour/ardour/system_exec.h b/libs/ardour/ardour/system_exec.h new file mode 100644 index 0000000000..ae865c7bff --- /dev/null +++ b/libs/ardour/ardour/system_exec.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2010 Paul Davis + Copyright (C) 2010-2014 Robin Gareus + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef _ardour_system_exec_h_ +#define _ardour_system_exec_h_ + +#include "ardour/libardour_visibility.h" +#include "pbd/system_exec.h" + +namespace ARDOUR { + +class LIBARDOUR_API SystemExec + : public PBD::SystemExec +{ + +public: + SystemExec (std::string c, std::string a = ""); + SystemExec (std::string c, char ** a); + SystemExec (std::string c, const std::map subs); + ~SystemExec (); + + int start (int stderr_mode = 1) { + return PBD::SystemExec::start(stderr_mode, _vfork_exec_wrapper); + } + +private: + static char * _vfork_exec_wrapper; + +}; /* end class */ + +}; /* end namespace */ + +#endif /* _libpbd_system_exec_h_ */ + + diff --git a/libs/ardour/system_exec.cc b/libs/ardour/system_exec.cc new file mode 100644 index 0000000000..760a9b7878 --- /dev/null +++ b/libs/ardour/system_exec.cc @@ -0,0 +1,78 @@ +/* + Copyright (C) 2010 Paul Davis + Copyright (C) 2010-2014 Robin Gareus + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include "pbd/pathscanner.h" +#include "pbd/file_utils.h" +#include "pbd/error.h" + +#include "ardour/filesystem_paths.h" +#include "ardour/system_exec.h" + +using namespace ARDOUR; + +char * SystemExec::_vfork_exec_wrapper = NULL; + +static char *vfork_exec_wrapper_path() { +#ifdef PLATFORM_WINDOWS + return NULL; +#else + std::string vfork_exec_wrapper; + if (!PBD::find_file_in_search_path ( + PBD::Searchpath(Glib::build_filename(ARDOUR::ardour_dll_directory(), "vfork")), + "ardour-exec-wrapper", vfork_exec_wrapper)) { + PBD::warning << "vfork exec wrapper not found..'" << endmsg; + return NULL; + } + return strdup(vfork_exec_wrapper.c_str()); +#endif +} + +SystemExec::SystemExec (std::string c, char ** a) + : PBD::SystemExec(c, a) +{ +#ifndef PLATFORM_WINDOWS + if (!_vfork_exec_wrapper) { + _vfork_exec_wrapper = vfork_exec_wrapper_path(); + } +#endif +} + +SystemExec::SystemExec (std::string c, std::string a) + : PBD::SystemExec(c, a) +{ +#ifndef PLATFORM_WINDOWS + if (!_vfork_exec_wrapper) { + _vfork_exec_wrapper = vfork_exec_wrapper_path(); + } +#endif +} + +SystemExec::SystemExec (std::string c, const std::map subs) + : PBD::SystemExec(c, subs) +{ +#ifndef PLATFORM_WINDOWS + if (!_vfork_exec_wrapper) { + _vfork_exec_wrapper = vfork_exec_wrapper_path(); + } +#endif +} + +SystemExec::~SystemExec() { } From 0cecd2f7d5c9876b587fafb596342312261970ba Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 19 May 2014 19:52:32 +0100 Subject: [PATCH 19/20] Use libardour SystemExec wrapper in gtk2_ardour #include ardour/system_exec.h where SystemExec is used in gtk2_ardour to get the libardour wrapper class, and remove the include completely where it's no longer required. --- gtk2_ardour/ardour_ui.cc | 1 - gtk2_ardour/transcode_ffmpeg.h | 2 +- gtk2_ardour/video_monitor.h | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 262e8f990c..fdfdc9fdad 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -123,7 +123,6 @@ typedef uint64_t microseconds_t; #include "video_server_dialog.h" #include "add_video_dialog.h" #include "transcode_video_dialog.h" -#include "pbd/system_exec.h" #include "i18n.h" diff --git a/gtk2_ardour/transcode_ffmpeg.h b/gtk2_ardour/transcode_ffmpeg.h index e25a68da91..ec01112bb3 100644 --- a/gtk2_ardour/transcode_ffmpeg.h +++ b/gtk2_ardour/transcode_ffmpeg.h @@ -21,8 +21,8 @@ #define __ardour_transcode_ffmpeg_h__ #include +#include "ardour/system_exec.h" #include "ardour/types.h" -#include "pbd/system_exec.h" /** @class TranscodeFfmpeg diff --git a/gtk2_ardour/video_monitor.h b/gtk2_ardour/video_monitor.h index 80273cf5e3..5f3a4b3850 100644 --- a/gtk2_ardour/video_monitor.h +++ b/gtk2_ardour/video_monitor.h @@ -26,7 +26,7 @@ #include "ardour/types.h" #include "ardour/session.h" #include "ardour/session_handle.h" -#include "pbd/system_exec.h" +#include "ardour/system_exec.h" namespace ARDOUR { class Session; From c464feb6aa96226856305531fedb8fba2e06689e Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 19 May 2014 20:50:37 +0100 Subject: [PATCH 20/20] indent in wscript uses spaces --- gtk2_ardour/wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index aac1f3a49b..facc129714 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -211,7 +211,7 @@ gtk2_ardour_sources = [ 'shuttle_control.cc', 'simpleline.cc', 'simplerect.cc', - 'soundcloud_export_selector.cc', + 'soundcloud_export_selector.cc', 'splash.cc', 'speaker_dialog.cc', 'startup.cc',