From a3465ff5d3b830e79a0e383b3c85df0c8cc3af9e Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 7 Oct 2013 14:44:35 +0100 Subject: 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 --- libs/ardour/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 ++- libs/ardour/soundcloud_upload.cc | 349 +++++++++++++++++++++++ libs/ardour/wscript | 1 + 7 files changed, 466 insertions(+), 4 deletions(-) create mode 100644 libs/ardour/ardour/soundcloud_upload.h create mode 100644 libs/ardour/soundcloud_upload.cc (limited to 'libs/ardour') 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/libs/ardour/soundcloud_upload.cc b/libs/ardour/soundcloud_upload.cc new file mode 100644 index 0000000000..f003d5ab65 --- /dev/null +++ b/libs/ardour/soundcloud_upload.cc @@ -0,0 +1,349 @@ +/* soundcloud_export.cpp ********************************************************************** + + Adapted for Ardour by Ben Loftis, March 2012 + + Licence GPL: + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +*************************************************************************************/ +#include "ardour/soundcloud_upload.h" + +#include "pbd/xml++.h" +#include +//#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(); +} + +std::string +SoundcloudUploader::Get_Auth_Token( std::string username, std::string password ) +{ + struct MemoryStruct xml_page; + xml_page.memory = NULL; + xml_page.size = 0; + + setcUrlOptions(); + + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page); + + struct curl_httppost *formpost=NULL; + struct curl_httppost *lastptr=NULL; + + /* Fill in the filename field */ + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "client_id", + CURLFORM_COPYCONTENTS, "e7ac891eef866f139773cf8102b7a719", + CURLFORM_END); + + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "client_secret", + CURLFORM_COPYCONTENTS, "d78f34d19f09d26731801a0cb0f382c4", + CURLFORM_END); + + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "grant_type", + CURLFORM_COPYCONTENTS, "password", + CURLFORM_END); + + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "username", + CURLFORM_COPYCONTENTS, username.c_str(), + CURLFORM_END); + + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "password", + CURLFORM_COPYCONTENTS, password.c_str(), + CURLFORM_END); + + struct curl_slist *headerlist=NULL; + headerlist = curl_slist_append(headerlist, "Expect:"); + headerlist = curl_slist_append(headerlist, "Accept: application/xml"); + curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist); + + /* what URL that receives this POST */ + std::string url = "https://api.soundcloud.com/oauth2/token"; + curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost); + + // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); + + // perform online request + CURLcode res = curl_easy_perform(curl_handle); + if( res != 0 ) { + std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl; + return ""; + } + + if(xml_page.memory){ + //cheesy way to parse the json return value. find access_token, then advance 3 quotes + + if ( strstr ( xml_page.memory , "access_token" ) == NULL) { + error << _("Upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg; + return ""; + } + + std::string token = strtok( xml_page.memory, "access_token" ); + token = strtok( NULL, "\"" ); + token = strtok( NULL, "\"" ); + token = strtok( NULL, "\"" ); + + free( xml_page.memory ); + return token; + } + + return ""; +} + +int +SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow) +{ + SoundcloudUploader *scu = (SoundcloudUploader *) caller; + std::cerr << scu->title << ": uploaded " << ulnow << " of " << ultotal << std::endl; + scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */ + return 0; +} + + +std::string +SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller) +{ + int still_running; + + struct MemoryStruct xml_page; + xml_page.memory = NULL; + xml_page.size = 0; + + setcUrlOptions(); + + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page); + + struct curl_httppost *formpost=NULL; + struct curl_httppost *lastptr=NULL; + + /* Fill in the file upload field. This makes libcurl load data from + the given file name when curl_easy_perform() is called. */ + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "track[asset_data]", + CURLFORM_FILE, file_path.c_str(), + CURLFORM_END); + + /* Fill in the filename field */ + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "oauth_token", + CURLFORM_COPYCONTENTS, token.c_str(), + CURLFORM_END); + + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "track[title]", + CURLFORM_COPYCONTENTS, title.c_str(), + CURLFORM_END); + + curl_formadd(&formpost, + &lastptr, + CURLFORM_COPYNAME, "track[sharing]", + CURLFORM_COPYCONTENTS, ispublic ? "public" : "private", + CURLFORM_END); + + /* initalize custom header list (stating that Expect: 100-continue is not + wanted */ + struct curl_slist *headerlist=NULL; + static const char buf[] = "Expect:"; + headerlist = curl_slist_append(headerlist, buf); + + + if (curl_handle && multi_handle) { + + /* what URL that receives this POST */ + std::string url = "https://api.soundcloud.com/tracks"; + curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); + // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); + + curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist); + curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost); + + this->title = title; // save title to show in progress bar + this->caller = caller; + + curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar + curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback); + curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this); + + curl_multi_add_handle(multi_handle, curl_handle); + + curl_multi_perform(multi_handle, &still_running); + + + while(still_running) { + struct timeval timeout; + int rc; /* select() return code */ + + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + int maxfd = -1; + + long curl_timeo = -1; + + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + /* set a suitable timeout to play around with */ + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + curl_multi_timeout(multi_handle, &curl_timeo); + if(curl_timeo >= 0) { + timeout.tv_sec = curl_timeo / 1000; + if(timeout.tv_sec > 1) + timeout.tv_sec = 1; + else + timeout.tv_usec = (curl_timeo % 1000) * 1000; + } + + /* get file descriptors from the transfers */ + curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); + + /* In a real-world program you OF COURSE check the return code of the + function calls. On success, the value of maxfd is guaranteed to be + greater or equal than -1. We call select(maxfd + 1, ...), specially in + case of (maxfd == -1), we call select(0, ...), which is basically equal + to sleep. */ + + rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); + + switch(rc) { + case -1: + /* select error */ + break; + case 0: + default: + /* timeout or readable/writable sockets */ + curl_multi_perform(multi_handle, &still_running); + break; + } + } + + /* then cleanup the formpost chain */ + curl_formfree(formpost); + + /* free slist */ + curl_slist_free_all (headerlist); + } + + curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar + + if(xml_page.memory){ + + std::cout << xml_page.memory << std::endl; + + XMLTree doc; + doc.read_buffer( xml_page.memory ); + XMLNode *root = doc.root(); + + if (!root) { + std::cout << "no root XML node!" << std::endl; + return ""; + } + + XMLNode *url_node = root->child("permalink-url"); + if (!url_node) { + std::cout << "no child node \"permalink-url\" found!" << std::endl; + return ""; + } + + XMLNode *text_node = url_node->child("text"); + if (!text_node) { + std::cout << "no text node found!" << std::endl; + return ""; + } + + free( xml_page.memory ); + return text_node->content(); + } + + return ""; +}; + + +SoundcloudUploader:: ~SoundcloudUploader() +{ + curl_easy_cleanup(curl_handle); + curl_multi_cleanup(multi_handle); +} + + +void +SoundcloudUploader::setcUrlOptions() +{ + // basic init for curl + curl_global_init(CURL_GLOBAL_ALL); + // some servers don't like requests that are made without a user-agent field, so we provide one + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + // setup curl error buffer + curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer); + // Allow redirection + curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1); + + // Allow connections to time out (without using signals) + curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30); + + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0); +} + 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', -- cgit v1.2.3 From 8b9a1fae6a580b526b3fc784581e6d0c8031b6db Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 7 Oct 2013 20:22:56 +0100 Subject: 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(+) (limited to 'libs/ardour') 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); -- cgit v1.2.3 From 2a93f7a25e63c796745c4a34ebc13239382ba2df Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Mon, 7 Oct 2013 20:28:13 +0100 Subject: 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(+) (limited to 'libs/ardour') 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); -- cgit v1.2.3 From cac644270a8626904a4a21f527cdcf5083defa60 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Thu, 10 Oct 2013 19:54:22 +0100 Subject: 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(-) (limited to 'libs/ardour') 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()) { -- cgit v1.2.3 From ba08ae96419d0605eb05921a7428173f48f0fbe0 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sun, 18 May 2014 22:33:25 +0100 Subject: 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(-) (limited to 'libs/ardour') 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 -- cgit v1.2.3 From 65dc32f9ec083c763761c79fb9306032e32e89b5 Mon Sep 17 00:00:00 2001 From: Colin Fletcher Date: Sun, 18 May 2014 23:02:24 +0100 Subject: 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 (limited to 'libs/ardour') 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() { } -- cgit v1.2.3