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/soundcloud_upload.h"
25 #include "pbd/xml++.h"
26 #include <pbd/error.h>
27 //#include "pbd/filesystem.h"
30 #include <sys/types.h>
32 #include <glib/gstdio.h>
38 // static const std::string base_url = "http://api.soundcloud.com/tracks/13158665?client_id=";
41 WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
43 register int realsize = (int)(size * nmemb);
44 struct MemoryStruct *mem = (struct MemoryStruct *)data;
46 mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
49 memcpy(&(mem->memory[mem->size]), ptr, realsize);
50 mem->size += realsize;
51 mem->memory[mem->size] = 0;
56 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, "e7ac891eef866f139773cf8102b7a719",
84 curl_formadd(&formpost,
86 CURLFORM_COPYNAME, "client_secret",
87 CURLFORM_COPYCONTENTS, "d78f34d19f09d26731801a0cb0f382c4",
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 std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl;
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 std::cerr << scu->title << ": uploaded " << ulnow << " of " << ultotal << std::endl;
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, 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 /* initalize custom header list (stating that Expect: 100-continue is not
203 struct curl_slist *headerlist=NULL;
204 static const char buf[] = "Expect:";
205 headerlist = curl_slist_append(headerlist, buf);
208 if (curl_handle && multi_handle) {
210 /* what URL that receives this POST */
211 std::string url = "https://api.soundcloud.com/tracks";
212 curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
213 // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
215 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
216 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
218 this->title = title; // save title to show in progress bar
219 this->caller = caller;
221 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
222 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
223 curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
225 curl_multi_add_handle(multi_handle, curl_handle);
227 curl_multi_perform(multi_handle, &still_running);
230 while(still_running) {
231 struct timeval timeout;
232 int rc; /* select() return code */
239 long curl_timeo = -1;
245 /* set a suitable timeout to play around with */
249 curl_multi_timeout(multi_handle, &curl_timeo);
250 if(curl_timeo >= 0) {
251 timeout.tv_sec = curl_timeo / 1000;
252 if(timeout.tv_sec > 1)
255 timeout.tv_usec = (curl_timeo % 1000) * 1000;
258 /* get file descriptors from the transfers */
259 curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
261 /* In a real-world program you OF COURSE check the return code of the
262 function calls. On success, the value of maxfd is guaranteed to be
263 greater or equal than -1. We call select(maxfd + 1, ...), specially in
264 case of (maxfd == -1), we call select(0, ...), which is basically equal
267 rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
275 /* timeout or readable/writable sockets */
276 curl_multi_perform(multi_handle, &still_running);
281 /* then cleanup the formpost chain */
282 curl_formfree(formpost);
285 curl_slist_free_all (headerlist);
288 curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
292 std::cout << xml_page.memory << std::endl;
295 doc.read_buffer( xml_page.memory );
296 XMLNode *root = doc.root();
299 std::cout << "no root XML node!" << std::endl;
303 XMLNode *url_node = root->child("permalink-url");
305 std::cout << "no child node \"permalink-url\" found!" << std::endl;
309 XMLNode *text_node = url_node->child("text");
311 std::cout << "no text node found!" << std::endl;
315 free( xml_page.memory );
316 return text_node->content();
323 SoundcloudUploader:: ~SoundcloudUploader()
325 curl_easy_cleanup(curl_handle);
326 curl_multi_cleanup(multi_handle);
331 SoundcloudUploader::setcUrlOptions()
333 // basic init for curl
334 curl_global_init(CURL_GLOBAL_ALL);
335 // some servers don't like requests that are made without a user-agent field, so we provide one
336 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
337 // setup curl error buffer
338 curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
340 curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
342 // Allow connections to time out (without using signals)
343 curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
344 curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
346 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
347 curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);