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>
30 #include <sys/types.h>
32 #include <glib/gstdio.h>
39 WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
41 register int realsize = (int)(size * nmemb);
42 struct MemoryStruct *mem = (struct MemoryStruct *)data;
44 mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
47 memcpy(&(mem->memory[mem->size]), ptr, realsize);
48 mem->size += realsize;
49 mem->memory[mem->size] = 0;
54 SoundcloudUploader::SoundcloudUploader()
56 curl_handle = curl_easy_init();
57 multi_handle = curl_multi_init();
61 SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
63 struct MemoryStruct xml_page;
64 xml_page.memory = NULL;
69 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
70 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
72 struct curl_httppost *formpost=NULL;
73 struct curl_httppost *lastptr=NULL;
75 /* Fill in the filename field */
76 curl_formadd(&formpost,
78 CURLFORM_COPYNAME, "client_id",
79 CURLFORM_COPYCONTENTS, "6dd9cf0ad281aa57e07745082cec580b",
82 curl_formadd(&formpost,
84 CURLFORM_COPYNAME, "client_secret",
85 CURLFORM_COPYCONTENTS, "53f5b0113fb338800f8a7a9904fc3569",
88 curl_formadd(&formpost,
90 CURLFORM_COPYNAME, "grant_type",
91 CURLFORM_COPYCONTENTS, "password",
94 curl_formadd(&formpost,
96 CURLFORM_COPYNAME, "username",
97 CURLFORM_COPYCONTENTS, username.c_str(),
100 curl_formadd(&formpost,
102 CURLFORM_COPYNAME, "password",
103 CURLFORM_COPYCONTENTS, password.c_str(),
106 struct curl_slist *headerlist=NULL;
107 headerlist = curl_slist_append(headerlist, "Expect:");
108 headerlist = curl_slist_append(headerlist, "Accept: application/xml");
109 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
111 /* what URL that receives this POST */
112 std::string url = "https://api.soundcloud.com/oauth2/token";
113 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
114 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
116 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
118 // perform online request
119 CURLcode res = curl_easy_perform(curl_handle);
121 DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("curl error %1 (%2)", res, curl_easy_strerror(res) ) );
125 if (xml_page.memory){
126 // cheesy way to parse the json return value. find access_token, then advance 3 quotes
128 if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
129 error << _("Upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
133 std::string token = strtok( xml_page.memory, "access_token" );
134 token = strtok( NULL, "\"" );
135 token = strtok( NULL, "\"" );
136 token = strtok( NULL, "\"" );
138 free( xml_page.memory );
146 SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
148 SoundcloudUploader *scu = (SoundcloudUploader *) caller;
149 DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("%1: uploaded %2 of %3", scu->title, ulnow, ultotal) );
150 scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
156 SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller)
160 struct MemoryStruct xml_page;
161 xml_page.memory = NULL;
166 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
167 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
169 struct curl_httppost *formpost=NULL;
170 struct curl_httppost *lastptr=NULL;
172 /* Fill in the file upload field. This makes libcurl load data from
173 the given file name when curl_easy_perform() is called. */
174 curl_formadd(&formpost,
176 CURLFORM_COPYNAME, "track[asset_data]",
177 CURLFORM_FILE, file_path.c_str(),
180 /* Fill in the filename field */
181 curl_formadd(&formpost,
183 CURLFORM_COPYNAME, "oauth_token",
184 CURLFORM_COPYCONTENTS, token.c_str(),
187 curl_formadd(&formpost,
189 CURLFORM_COPYNAME, "track[title]",
190 CURLFORM_COPYCONTENTS, title.c_str(),
193 curl_formadd(&formpost,
195 CURLFORM_COPYNAME, "track[sharing]",
196 CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
199 curl_formadd(&formpost,
201 CURLFORM_COPYNAME, "track[downloadable]",
202 CURLFORM_COPYCONTENTS, downloadable ? "true" : "false",
207 /* initalize custom header list (stating that Expect: 100-continue is not
209 struct curl_slist *headerlist=NULL;
210 static const char buf[] = "Expect:";
211 headerlist = curl_slist_append(headerlist, buf);
214 if (curl_handle && multi_handle) {
216 /* what URL that receives this POST */
217 std::string url = "https://api.soundcloud.com/tracks";
218 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
219 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
221 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
222 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
224 this->title = title; // save title to show in progress bar
225 this->caller = caller;
227 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
228 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
229 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
231 curl_multi_add_handle(multi_handle, curl_handle);
233 curl_multi_perform(multi_handle, &still_running);
236 while(still_running) {
237 struct timeval timeout;
238 int rc; /* select() return code */
245 long curl_timeo = -1;
251 /* set a suitable timeout to play around with */
255 curl_multi_timeout(multi_handle, &curl_timeo);
256 if(curl_timeo >= 0) {
257 timeout.tv_sec = curl_timeo / 1000;
258 if(timeout.tv_sec > 1)
261 timeout.tv_usec = (curl_timeo % 1000) * 1000;
264 /* get file descriptors from the transfers */
265 curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
267 /* In a real-world program you OF COURSE check the return code of the
268 function calls. On success, the value of maxfd is guaranteed to be
269 greater or equal than -1. We call select(maxfd + 1, ...), specially in
270 case of (maxfd == -1), we call select(0, ...), which is basically equal
273 rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
281 /* timeout or readable/writable sockets */
282 curl_multi_perform(multi_handle, &still_running);
287 /* then cleanup the formpost chain */
288 curl_formfree(formpost);
291 curl_slist_free_all (headerlist);
294 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
298 DEBUG_TRACE (DEBUG::Soundcloud, xml_page.memory);
301 doc.read_buffer( xml_page.memory );
302 XMLNode *root = doc.root();
305 DEBUG_TRACE (DEBUG::Soundcloud, "no root XML node!");
309 XMLNode *url_node = root->child("permalink-url");
311 DEBUG_TRACE (DEBUG::Soundcloud, "no child node \"permalink-url\" found!");
315 XMLNode *text_node = url_node->child("text");
317 DEBUG_TRACE (DEBUG::Soundcloud, "no text node found!");
321 free( xml_page.memory );
322 return text_node->content();
329 SoundcloudUploader:: ~SoundcloudUploader()
331 curl_easy_cleanup(curl_handle);
332 curl_multi_cleanup(multi_handle);
337 SoundcloudUploader::setcUrlOptions()
339 // basic init for curl
340 curl_global_init(CURL_GLOBAL_ALL);
341 // some servers don't like requests that are made without a user-agent field, so we provide one
342 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
343 // setup curl error buffer
344 curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
346 curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
348 // Allow connections to time out (without using signals)
349 curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
350 curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
352 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
353 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);