summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
Diffstat (limited to 'libs/ardour')
-rw-r--r--libs/ardour/ardour/export_format_specification.h3
-rw-r--r--libs/ardour/ardour/export_handler.h14
-rw-r--r--libs/ardour/ardour/soundcloud_upload.h55
-rw-r--r--libs/ardour/export_format_specification.cc11
-rw-r--r--libs/ardour/export_handler.cc37
-rw-r--r--libs/ardour/soundcloud_upload.cc349
-rw-r--r--libs/ardour/wscript1
7 files changed, 466 insertions, 4 deletions
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<void, double, double, std::string> SoundcloudProgress;
+
+ /* upload credentials & preferences */
+ std::string upload_username;
+ std::string upload_password;
+ bool upload_public;
+ bool upload_open;
+
private:
void handle_duplicate_format_extensions();
diff --git a/libs/ardour/ardour/soundcloud_upload.h b/libs/ardour/ardour/soundcloud_upload.h
new file mode 100644
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 <string>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "curl/curl.h"
+#include "ardour/session_handle.h"
+#include "ardour/export_handler.h"
+#include "pbd/signals.h"
+
+//--- struct to store XML file
+struct MemoryStruct {
+ char *memory;
+ size_t size;
+};
+
+
+class SoundcloudUploader
+{
+public:
+ SoundcloudUploader();
+ ~SoundcloudUploader();
+
+ std::string Get_Auth_Token(std::string username, std::string password);
+ std::string Upload (std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller);
+ static int progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow);
+
+
+private:
+
+ void setcUrlOptions();
+
+ CURL *curl_handle;
+ CURLM *multi_handle;
+ char errorBuffer[CURL_ERROR_SIZE]; // storage for cUrl error message
+
+ std::string title;
+ ARDOUR::ExportHandler *caller;
+
+};
+
+#endif /* __ardour_soundcloud_upload_h__ */
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 <pbd/error.h>
+//#include "pbd/filesystem.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+// static const std::string base_url = "http://api.soundcloud.com/tracks/13158665?client_id=";
+
+size_t
+WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ register int realsize = (int)(size * nmemb);
+ struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+ mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+
+ if (mem->memory) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+ return realsize;
+}
+
+SoundcloudUploader::SoundcloudUploader()
+{
+ curl_handle = curl_easy_init();
+ multi_handle = curl_multi_init();
+}
+
+std::string
+SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
+{
+ struct MemoryStruct xml_page;
+ xml_page.memory = NULL;
+ xml_page.size = 0;
+
+ setcUrlOptions();
+
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+ struct curl_httppost *formpost=NULL;
+ struct curl_httppost *lastptr=NULL;
+
+ /* Fill in the filename field */
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "client_id",
+ CURLFORM_COPYCONTENTS, "e7ac891eef866f139773cf8102b7a719",
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "client_secret",
+ CURLFORM_COPYCONTENTS, "d78f34d19f09d26731801a0cb0f382c4",
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "grant_type",
+ CURLFORM_COPYCONTENTS, "password",
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "username",
+ CURLFORM_COPYCONTENTS, username.c_str(),
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "password",
+ CURLFORM_COPYCONTENTS, password.c_str(),
+ CURLFORM_END);
+
+ struct curl_slist *headerlist=NULL;
+ headerlist = curl_slist_append(headerlist, "Expect:");
+ headerlist = curl_slist_append(headerlist, "Accept: application/xml");
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+
+ /* what URL that receives this POST */
+ std::string url = "https://api.soundcloud.com/oauth2/token";
+ curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+ // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+ // perform online request
+ CURLcode res = curl_easy_perform(curl_handle);
+ if( res != 0 ) {
+ std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl;
+ return "";
+ }
+
+ if(xml_page.memory){
+ //cheesy way to parse the json return value. find access_token, then advance 3 quotes
+
+ if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
+ error << _("Upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
+ return "";
+ }
+
+ std::string token = strtok( xml_page.memory, "access_token" );
+ token = strtok( NULL, "\"" );
+ token = strtok( NULL, "\"" );
+ token = strtok( NULL, "\"" );
+
+ free( xml_page.memory );
+ return token;
+ }
+
+ return "";
+}
+
+int
+SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
+{
+ SoundcloudUploader *scu = (SoundcloudUploader *) caller;
+ std::cerr << scu->title << ": uploaded " << ulnow << " of " << ultotal << std::endl;
+ scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
+ return 0;
+}
+
+
+std::string
+SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller)
+{
+ int still_running;
+
+ struct MemoryStruct xml_page;
+ xml_page.memory = NULL;
+ xml_page.size = 0;
+
+ setcUrlOptions();
+
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+ struct curl_httppost *formpost=NULL;
+ struct curl_httppost *lastptr=NULL;
+
+ /* Fill in the file upload field. This makes libcurl load data from
+ the given file name when curl_easy_perform() is called. */
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "track[asset_data]",
+ CURLFORM_FILE, file_path.c_str(),
+ CURLFORM_END);
+
+ /* Fill in the filename field */
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "oauth_token",
+ CURLFORM_COPYCONTENTS, token.c_str(),
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "track[title]",
+ CURLFORM_COPYCONTENTS, title.c_str(),
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "track[sharing]",
+ CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
+ CURLFORM_END);
+
+ /* initalize custom header list (stating that Expect: 100-continue is not
+ wanted */
+ struct curl_slist *headerlist=NULL;
+ static const char buf[] = "Expect:";
+ headerlist = curl_slist_append(headerlist, buf);
+
+
+ if (curl_handle && multi_handle) {
+
+ /* what URL that receives this POST */
+ std::string url = "https://api.soundcloud.com/tracks";
+ curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+ // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+ this->title = title; // save title to show in progress bar
+ this->caller = caller;
+
+ curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
+ curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
+ curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
+
+ curl_multi_add_handle(multi_handle, curl_handle);
+
+ curl_multi_perform(multi_handle, &still_running);
+
+
+ while(still_running) {
+ struct timeval timeout;
+ int rc; /* select() return code */
+
+ fd_set fdread;
+ fd_set fdwrite;
+ fd_set fdexcep;
+ int maxfd = -1;
+
+ long curl_timeo = -1;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+ FD_ZERO(&fdexcep);
+
+ /* set a suitable timeout to play around with */
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ curl_multi_timeout(multi_handle, &curl_timeo);
+ if(curl_timeo >= 0) {
+ timeout.tv_sec = curl_timeo / 1000;
+ if(timeout.tv_sec > 1)
+ timeout.tv_sec = 1;
+ else
+ timeout.tv_usec = (curl_timeo % 1000) * 1000;
+ }
+
+ /* get file descriptors from the transfers */
+ curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
+
+ /* In a real-world program you OF COURSE check the return code of the
+ function calls. On success, the value of maxfd is guaranteed to be
+ greater or equal than -1. We call select(maxfd + 1, ...), specially in
+ case of (maxfd == -1), we call select(0, ...), which is basically equal
+ to sleep. */
+
+ rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+
+ switch(rc) {
+ case -1:
+ /* select error */
+ break;
+ case 0:
+ default:
+ /* timeout or readable/writable sockets */
+ curl_multi_perform(multi_handle, &still_running);
+ break;
+ }
+ }
+
+ /* then cleanup the formpost chain */
+ curl_formfree(formpost);
+
+ /* free slist */
+ curl_slist_free_all (headerlist);
+ }
+
+ curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
+
+ if(xml_page.memory){
+
+ std::cout << xml_page.memory << std::endl;
+
+ XMLTree doc;
+ doc.read_buffer( xml_page.memory );
+ XMLNode *root = doc.root();
+
+ if (!root) {
+ std::cout << "no root XML node!" << std::endl;
+ return "";
+ }
+
+ XMLNode *url_node = root->child("permalink-url");
+ if (!url_node) {
+ std::cout << "no child node \"permalink-url\" found!" << std::endl;
+ return "";
+ }
+
+ XMLNode *text_node = url_node->child("text");
+ if (!text_node) {
+ std::cout << "no text node found!" << std::endl;
+ return "";
+ }
+
+ free( xml_page.memory );
+ return text_node->content();
+ }
+
+ return "";
+};
+
+
+SoundcloudUploader:: ~SoundcloudUploader()
+{
+ curl_easy_cleanup(curl_handle);
+ curl_multi_cleanup(multi_handle);
+}
+
+
+void
+SoundcloudUploader::setcUrlOptions()
+{
+ // basic init for curl
+ curl_global_init(CURL_GLOBAL_ALL);
+ // some servers don't like requests that are made without a user-agent field, so we provide one
+ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+ // setup curl error buffer
+ curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
+ // Allow redirection
+ curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
+
+ // Allow connections to time out (without using signals)
+ curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
+
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
+}
+
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',