1 /* soundcloud_export.cpp **********************************************************************
3 Adapted for Ardour by Ben Loftis, March 2012
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 *************************************************************************************/
23 #include "ardour/debug.h"
24 #include "ardour/soundcloud_upload.h"
26 #include "pbd/xml++.h"
27 #include <pbd/error.h>
28 //#include "pbd/filesystem.h"
31 #include <sys/types.h>
33 #include <glib/gstdio.h>
39 // static const std::string base_url = "http://api.soundcloud.com/tracks/13158665?client_id=";
42 WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
44 register int realsize = (int)(size * nmemb);
45 struct MemoryStruct *mem = (struct MemoryStruct *)data;
47 mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
50 memcpy(&(mem->memory[mem->size]), ptr, realsize);
51 mem->size += realsize;
52 mem->memory[mem->size] = 0;
57 SoundcloudUploader::SoundcloudUploader()
59 curl_handle = curl_easy_init();
60 multi_handle = curl_multi_init();
64 SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
66 struct MemoryStruct xml_page;
67 xml_page.memory = NULL;
72 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
73 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
75 struct curl_httppost *formpost=NULL;
76 struct curl_httppost *lastptr=NULL;
78 /* Fill in the filename field */
79 curl_formadd(&formpost,
81 CURLFORM_COPYNAME, "client_id",
82 CURLFORM_COPYCONTENTS, "e7ac891eef866f139773cf8102b7a719",
85 curl_formadd(&formpost,
87 CURLFORM_COPYNAME, "client_secret",
88 CURLFORM_COPYCONTENTS, "d78f34d19f09d26731801a0cb0f382c4",
91 curl_formadd(&formpost,
93 CURLFORM_COPYNAME, "grant_type",
94 CURLFORM_COPYCONTENTS, "password",
97 curl_formadd(&formpost,
99 CURLFORM_COPYNAME, "username",
100 CURLFORM_COPYCONTENTS, username.c_str(),
103 curl_formadd(&formpost,
105 CURLFORM_COPYNAME, "password",
106 CURLFORM_COPYCONTENTS, password.c_str(),
109 struct curl_slist *headerlist=NULL;
110 headerlist = curl_slist_append(headerlist, "Expect:");
111 headerlist = curl_slist_append(headerlist, "Accept: application/xml");
112 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
114 /* what URL that receives this POST */
115 std::string url = "https://api.soundcloud.com/oauth2/token";
116 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
117 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
119 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
121 // perform online request
122 CURLcode res = curl_easy_perform(curl_handle);
124 DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("curl error %1 (%2)", res, curl_easy_strerror(res) ) );
129 //cheesy way to parse the json return value. find access_token, then advance 3 quotes
131 if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
132 error << _("Upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
136 std::string token = strtok( xml_page.memory, "access_token" );
137 token = strtok( NULL, "\"" );
138 token = strtok( NULL, "\"" );
139 token = strtok( NULL, "\"" );
141 free( xml_page.memory );
149 SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
151 SoundcloudUploader *scu = (SoundcloudUploader *) caller;
152 DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("%1: uploaded %2 of %3", scu->title, ulnow, ultotal) );
153 scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
159 SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller)
163 struct MemoryStruct xml_page;
164 xml_page.memory = NULL;
169 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
170 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
172 struct curl_httppost *formpost=NULL;
173 struct curl_httppost *lastptr=NULL;
175 /* Fill in the file upload field. This makes libcurl load data from
176 the given file name when curl_easy_perform() is called. */
177 curl_formadd(&formpost,
179 CURLFORM_COPYNAME, "track[asset_data]",
180 CURLFORM_FILE, file_path.c_str(),
183 /* Fill in the filename field */
184 curl_formadd(&formpost,
186 CURLFORM_COPYNAME, "oauth_token",
187 CURLFORM_COPYCONTENTS, token.c_str(),
190 curl_formadd(&formpost,
192 CURLFORM_COPYNAME, "track[title]",
193 CURLFORM_COPYCONTENTS, title.c_str(),
196 curl_formadd(&formpost,
198 CURLFORM_COPYNAME, "track[sharing]",
199 CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
202 curl_formadd(&formpost,
204 CURLFORM_COPYNAME, "track[downloadable]",
205 CURLFORM_COPYCONTENTS, downloadable ? "true" : "false",
210 /* initalize custom header list (stating that Expect: 100-continue is not
212 struct curl_slist *headerlist=NULL;
213 static const char buf[] = "Expect:";
214 headerlist = curl_slist_append(headerlist, buf);
217 if (curl_handle && multi_handle) {
219 /* what URL that receives this POST */
220 std::string url = "https://api.soundcloud.com/tracks";
221 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
222 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
224 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
225 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
227 this->title = title; // save title to show in progress bar
228 this->caller = caller;
230 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
231 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
232 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
234 curl_multi_add_handle(multi_handle, curl_handle);
236 curl_multi_perform(multi_handle, &still_running);
239 while(still_running) {
240 struct timeval timeout;
241 int rc; /* select() return code */
248 long curl_timeo = -1;
254 /* set a suitable timeout to play around with */
258 curl_multi_timeout(multi_handle, &curl_timeo);
259 if(curl_timeo >= 0) {
260 timeout.tv_sec = curl_timeo / 1000;
261 if(timeout.tv_sec > 1)
264 timeout.tv_usec = (curl_timeo % 1000) * 1000;
267 /* get file descriptors from the transfers */
268 curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
270 /* In a real-world program you OF COURSE check the return code of the
271 function calls. On success, the value of maxfd is guaranteed to be
272 greater or equal than -1. We call select(maxfd + 1, ...), specially in
273 case of (maxfd == -1), we call select(0, ...), which is basically equal
276 rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
284 /* timeout or readable/writable sockets */
285 curl_multi_perform(multi_handle, &still_running);
290 /* then cleanup the formpost chain */
291 curl_formfree(formpost);
294 curl_slist_free_all (headerlist);
297 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
301 DEBUG_TRACE (DEBUG::Soundcloud, xml_page.memory);
304 doc.read_buffer( xml_page.memory );
305 XMLNode *root = doc.root();
308 DEBUG_TRACE (DEBUG::Soundcloud, "no root XML node!");
312 XMLNode *url_node = root->child("permalink-url");
314 DEBUG_TRACE (DEBUG::Soundcloud, "no child node \"permalink-url\" found!");
318 XMLNode *text_node = url_node->child("text");
320 DEBUG_TRACE (DEBUG::Soundcloud, "no text node found!");
324 free( xml_page.memory );
325 return text_node->content();
332 SoundcloudUploader:: ~SoundcloudUploader()
334 curl_easy_cleanup(curl_handle);
335 curl_multi_cleanup(multi_handle);
340 SoundcloudUploader::setcUrlOptions()
342 // basic init for curl
343 curl_global_init(CURL_GLOBAL_ALL);
344 // some servers don't like requests that are made without a user-agent field, so we provide one
345 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
346 // setup curl error buffer
347 curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
349 curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
351 // Allow connections to time out (without using signals)
352 curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
353 curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
355 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
356 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);