add various options for curl connections / fix write_data() prototype
[scrobby.git] / src / song.cpp
blob1a0c8fb6b84417e141e778caac3391a4189b1513
1 /***************************************************************************
2 * Copyright (C) 2008-2009 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <curl/curl.h>
22 #include <cstring>
23 #include <fstream>
24 #include <string>
26 #include "callback.h"
27 #include "misc.h"
28 #include "scrobby.h"
29 #include "song.h"
31 using std::string;
33 extern Handshake myHandshake;
35 std::deque<std::string> MPD::Song::Queue;
37 MPD::Song::Song() : Data(0),
38 StartTime(0),
39 Playback(0),
40 itsIsStream(0)
44 MPD::Song::~Song()
46 if (Data)
47 mpd_freeSong(Data);
50 void MPD::Song::Clear()
52 if (Data)
53 mpd_freeSong(Data);
54 Data = 0;
55 StartTime = 0;
56 Playback = 0;
57 itsIsStream = 0;
60 void MPD::Song::SetData(mpd_Song *song)
62 if (!song)
63 return;
64 if (Data)
65 mpd_freeSong(Data);
66 Data = song;
67 itsIsStream = strncmp("http://", Data->file, 7) == 0;
70 void MPD::Song::Submit()
72 if (!Data)
73 return;
75 if (itsIsStream)
76 Data->time = Playback;
78 if (canBeSubmitted())
80 if (!Queue.empty())
82 Log(llInfo, "Adding song to queue...");
83 Cache();
84 Clear();
85 SendQueue();
86 return;
89 if (myHandshake.Status != "OK" || myHandshake.SubmissionURL.empty())
91 Log(llInfo, "Problem with handshake, queue song at position %d...", Song::Queue.size());
92 Cache();
93 Clear();
94 return;
97 Log(llInfo, "Submitting song...");
99 std::ostringstream postdata;
100 string result, postdata_str;
101 CURLcode code;
103 char *c_artist = curl_easy_escape(0, Data->artist, 0);
104 char *c_title = curl_easy_escape(0, Data->title, 0);
105 char *c_album = Data->album ? curl_easy_escape(0, Data->album, 0) : NULL;
106 char *c_track = Data->track ? curl_easy_escape(0, Data->track, 0) : NULL;
107 char *c_mb_trackid = Data->musicbrainz_trackid ? curl_easy_escape(0, Data->musicbrainz_trackid, 0) : NULL;
109 postdata
110 << "s=" << myHandshake.SessionID
111 << "&a[0]=" << c_artist
112 << "&t[0]=" << c_title
113 << "&i[0]=" << StartTime
114 << "&o[0]=P"
115 << "&r[0]="
116 << "&l[0]=" << Data->time
117 << "&b[0]=";
118 if (c_album)
119 postdata << c_album;
120 postdata << "&n[0]=";
121 if (c_track)
122 postdata << c_track;
123 postdata << "&m[0]=";
124 if (c_mb_trackid)
125 postdata << c_mb_trackid;
127 curl_free(c_artist);
128 curl_free(c_title);
129 curl_free(c_album);
130 curl_free(c_track);
131 curl_free(c_mb_trackid);
133 postdata_str = postdata.str();
135 Log(llVerbose, "URL: %s", myHandshake.SubmissionURL.c_str());
136 Log(llVerbose, "Post data: %s", postdata_str.c_str());
138 CURL *submission = curl_easy_init();
139 curl_easy_setopt(submission, CURLOPT_URL, myHandshake.SubmissionURL.c_str());
140 curl_easy_setopt(submission, CURLOPT_POST, 1);
141 curl_easy_setopt(submission, CURLOPT_POSTFIELDS, postdata_str.c_str());
142 curl_easy_setopt(submission, CURLOPT_WRITEFUNCTION, write_data);
143 curl_easy_setopt(submission, CURLOPT_WRITEDATA, &result);
144 curl_easy_setopt(submission, CURLOPT_CONNECTTIMEOUT, curl_connecttimeout);
145 curl_easy_setopt(submission, CURLOPT_TIMEOUT, curl_timeout);
146 curl_easy_setopt(submission, CURLOPT_DNS_CACHE_TIMEOUT, 0);
147 curl_easy_setopt(submission, CURLOPT_NOPROGRESS, 1);
148 curl_easy_setopt(submission, CURLOPT_NOSIGNAL, 1);
149 code = curl_easy_perform(submission);
150 curl_easy_cleanup(submission);
152 IgnoreNewlines(result);
154 if (result == "OK")
156 Log(llInfo, "Song submitted.");
158 else
160 if (result.empty())
162 Log(llInfo, "Error while submitting song: %s", curl_easy_strerror(code));
164 else
166 Log(llInfo, "Audioscrobbler returned status %s", result.c_str());
167 myHandshake.Clear(); // handshake probably failed if we are here, so reset it
168 Log(llVerbose, "Handshake reset");
170 Cache();
173 Clear();
176 void MPD::Song::Cache()
178 std::ostringstream cache;
179 string cache_str;
181 char *c_artist = curl_easy_escape(0, Data->artist, 0);
182 char *c_title = curl_easy_escape(0, Data->title, 0);
183 char *c_album = Data->album ? curl_easy_escape(0, Data->album, 0) : NULL;
184 char *c_track = Data->track ? curl_easy_escape(0, Data->track, 0) : NULL;
185 char *c_mb_trackid = Data->musicbrainz_trackid ? curl_easy_escape(0, Data->musicbrainz_trackid, 0) : NULL;
187 cache
188 << "&a[" << Song::Queue.size() << "]=" << c_artist
189 << "&t[" << Song::Queue.size() << "]=" << c_title
190 << "&i[" << Song::Queue.size() << "]=" << StartTime
191 << "&o[" << Song::Queue.size() << "]=P"
192 << "&r[" << Song::Queue.size() << "]="
193 << "&l[" << Song::Queue.size() << "]=" << Data->time
194 << "&b[" << Song::Queue.size() << "]=";
195 if (c_album)
196 cache << c_album;
197 cache << "&n[" << Song::Queue.size() << "]=";
198 if (c_track)
199 cache << c_track;
200 cache << "&m[" << Song::Queue.size() << "]=";
201 if (c_mb_trackid)
202 cache << c_mb_trackid;
204 cache_str = cache.str();
206 Log(llVerbose, "Metadata: %s", cache_str.c_str());
208 curl_free(c_artist);
209 curl_free(c_title);
210 curl_free(c_album);
211 curl_free(c_track);
212 curl_free(c_mb_trackid);
214 WriteCache(cache_str);
215 Song::Queue.push_back(cache_str);
216 Log(llInfo, "Song cached.");
219 bool MPD::Song::isStream() const
221 return itsIsStream;
224 bool MPD::Song::canBeSubmitted()
226 if (!StartTime || Data->time < 30 || !Data->artist || !Data->title)
228 if (!StartTime)
230 Log(llInfo, "Song's start time wasn't known, not submitting.");
232 else if (Data->time < 30)
234 Log(llInfo, "Song's length is too short, not submitting.");
236 else if (!Data->artist || !Data->title)
238 Log(llInfo, "Song has missing tags, not submitting.");
240 return false;
242 else if (Playback < 4*60 && Playback < Data->time/2)
244 Log(llInfo, "Noticed playback was too short, not submitting.");
245 return false;
247 return true;
250 void MPD::Song::GetCached()
252 std::ifstream f(Config.file_cache.c_str());
253 if (f.is_open())
255 std::string line;
256 while (!f.eof())
258 getline(f, line);
259 if (!line.empty())
260 Queue.push_back(line);
265 void MPD::Song::SendQueue()
267 if (Song::Queue.empty())
268 return;
270 Log(llInfo, "Submitting songs from queue...");
272 string result, postdata;
273 CURLcode code;
275 postdata = "s=";
276 postdata += myHandshake.SessionID;
278 for (std::deque<string>::const_iterator it = Song::Queue.begin(); it != Song::Queue.end(); it++)
279 postdata += *it;
281 Log(llVerbose, "URL: %s", myHandshake.SubmissionURL.c_str());
282 Log(llVerbose, "Post data: %s", postdata.c_str());
284 CURL *submission = curl_easy_init();
285 curl_easy_setopt(submission, CURLOPT_URL, myHandshake.SubmissionURL.c_str());
286 curl_easy_setopt(submission, CURLOPT_POST, 1);
287 curl_easy_setopt(submission, CURLOPT_POSTFIELDS, postdata.c_str());
288 curl_easy_setopt(submission, CURLOPT_WRITEFUNCTION, write_data);
289 curl_easy_setopt(submission, CURLOPT_WRITEDATA, &result);
290 curl_easy_setopt(submission, CURLOPT_CONNECTTIMEOUT, curl_connecttimeout);
291 curl_easy_setopt(submission, CURLOPT_TIMEOUT, curl_timeout);
292 curl_easy_setopt(submission, CURLOPT_DNS_CACHE_TIMEOUT, 0);
293 curl_easy_setopt(submission, CURLOPT_NOPROGRESS, 1);
294 curl_easy_setopt(submission, CURLOPT_NOSIGNAL, 1);
295 code = curl_easy_perform(submission);
296 curl_easy_cleanup(submission);
298 IgnoreNewlines(result);
300 if (result == "OK")
302 Log(llInfo, "Number of submitted songs: %d", Song::Queue.size());
303 Song::Queue.clear();
304 ClearCache();
306 else
308 if (result.empty())
310 Log(llInfo, "Error while submitting songs: %s", curl_easy_strerror(code));
312 else
314 Log(llInfo, "Audioscrobbler returned status %s", result.c_str());