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 "pbd/gstdio_compat.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()
58 curl_handle = curl_easy_init();
59 multi_handle = curl_multi_init();
63 SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
65 struct MemoryStruct xml_page;
66 xml_page.memory = NULL;
71 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
72 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
74 struct curl_httppost *formpost=NULL;
75 struct curl_httppost *lastptr=NULL;
77 /* Fill in the filename field */
78 curl_formadd(&formpost,
80 CURLFORM_COPYNAME, "client_id",
81 CURLFORM_COPYCONTENTS, "6dd9cf0ad281aa57e07745082cec580b",
84 curl_formadd(&formpost,
86 CURLFORM_COPYNAME, "client_secret",
87 CURLFORM_COPYCONTENTS, "53f5b0113fb338800f8a7a9904fc3569",
90 curl_formadd(&formpost,
92 CURLFORM_COPYNAME, "grant_type",
93 CURLFORM_COPYCONTENTS, "password",
96 curl_formadd(&formpost,
98 CURLFORM_COPYNAME, "username",
99 CURLFORM_COPYCONTENTS, username.c_str(),
102 curl_formadd(&formpost,
104 CURLFORM_COPYNAME, "password",
105 CURLFORM_COPYCONTENTS, password.c_str(),
108 struct curl_slist *headerlist=NULL;
109 headerlist = curl_slist_append(headerlist, "Expect:");
110 headerlist = curl_slist_append(headerlist, "Accept: application/xml");
111 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
113 /* what URL that receives this POST */
114 std::string url = "https://api.soundcloud.com/oauth2/token";
115 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
116 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
118 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
120 // perform online request
121 CURLcode res = curl_easy_perform(curl_handle);
123 DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("curl error %1 (%2)\n", res, curl_easy_strerror(res) ) );
127 if (xml_page.memory){
128 // cheesy way to parse the json return value. find access_token, then advance 3 quotes
130 if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
131 error << _("Upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
135 std::string token = strtok( xml_page.memory, "access_token" );
136 token = strtok( NULL, "\"" );
137 token = strtok( NULL, "\"" );
138 token = strtok( NULL, "\"" );
140 free( xml_page.memory );
148 SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
150 SoundcloudUploader *scu = (SoundcloudUploader *) caller;
151 DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("%1: uploaded %2 of %3\n", scu->title, ulnow, ultotal) );
152 scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
158 SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller)
162 struct MemoryStruct xml_page;
163 xml_page.memory = NULL;
168 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
169 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
171 struct curl_httppost *formpost=NULL;
172 struct curl_httppost *lastptr=NULL;
174 /* Fill in the file upload field. This makes libcurl load data from
175 the given file name when curl_easy_perform() is called. */
176 curl_formadd(&formpost,
178 CURLFORM_COPYNAME, "track[asset_data]",
179 CURLFORM_FILE, file_path.c_str(),
182 /* Fill in the filename field */
183 curl_formadd(&formpost,
185 CURLFORM_COPYNAME, "oauth_token",
186 CURLFORM_COPYCONTENTS, token.c_str(),
189 curl_formadd(&formpost,
191 CURLFORM_COPYNAME, "track[title]",
192 CURLFORM_COPYCONTENTS, title.c_str(),
195 curl_formadd(&formpost,
197 CURLFORM_COPYNAME, "track[sharing]",
198 CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
201 curl_formadd(&formpost,
203 CURLFORM_COPYNAME, "track[downloadable]",
204 CURLFORM_COPYCONTENTS, downloadable ? "true" : "false",
209 /* initalize custom header list (stating that Expect: 100-continue is not
211 struct curl_slist *headerlist=NULL;
212 static const char buf[] = "Expect:";
213 headerlist = curl_slist_append(headerlist, buf);
216 if (curl_handle && multi_handle) {
218 /* what URL that receives this POST */
219 std::string url = "https://api.soundcloud.com/tracks";
220 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
221 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
223 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
224 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
226 this->title = title; // save title to show in progress bar
227 this->caller = caller;
229 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
230 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
231 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
233 curl_multi_add_handle(multi_handle, curl_handle);
235 curl_multi_perform(multi_handle, &still_running);
238 while(still_running) {
239 struct timeval timeout;
240 int rc; /* select() return code */
247 long curl_timeo = -1;
253 /* set a suitable timeout to play around with */
257 curl_multi_timeout(multi_handle, &curl_timeo);
258 if(curl_timeo >= 0) {
259 timeout.tv_sec = curl_timeo / 1000;
260 if(timeout.tv_sec > 1)
263 timeout.tv_usec = (curl_timeo % 1000) * 1000;
266 /* get file descriptors from the transfers */
267 curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
269 /* In a real-world program you OF COURSE check the return code of the
270 function calls. On success, the value of maxfd is guaranteed to be
271 greater or equal than -1. We call select(maxfd + 1, ...), specially in
272 case of (maxfd == -1), we call select(0, ...), which is basically equal
275 rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
283 /* timeout or readable/writable sockets */
284 curl_multi_perform(multi_handle, &still_running);
289 /* then cleanup the formpost chain */
290 curl_formfree(formpost);
293 curl_slist_free_all (headerlist);
296 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
300 DEBUG_TRACE (DEBUG::Soundcloud, xml_page.memory);
303 doc.read_buffer( xml_page.memory );
304 XMLNode *root = doc.root();
307 DEBUG_TRACE (DEBUG::Soundcloud, "no root XML node!\n");
311 XMLNode *url_node = root->child("permalink-url");
313 DEBUG_TRACE (DEBUG::Soundcloud, "no child node \"permalink-url\" found!\n");
317 XMLNode *text_node = url_node->child("text");
319 DEBUG_TRACE (DEBUG::Soundcloud, "no text node found!\n");
323 free( xml_page.memory );
324 return text_node->content();
331 SoundcloudUploader:: ~SoundcloudUploader()
333 curl_easy_cleanup(curl_handle);
334 curl_multi_cleanup(multi_handle);
339 SoundcloudUploader::setcUrlOptions()
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);