hopeful fix for random crashes - part I
[scrobby.git] / src / song.cpp
bloba69dac5a5ef5f59d6e2f8ec676a0f6a1778c0e59
1 /***************************************************************************
2 * Copyright (C) 2008 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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <curl/curl.h>
22 #include <cstring>
23 #include <string>
24 #include <vector>
26 #include "misc.h"
27 #include "scrobby.h"
28 #include "song.h"
30 using std::string;
32 extern Handshake handshake;
34 extern pthread_mutex_t curl_lock;
35 extern pthread_mutex_t handshake_lock;
37 extern std::vector<string> SongsQueue;
39 MPD::Song::Song() : itsSong(0),
40 itsStartTime(0),
41 itsNoticedPlayback(0),
42 itsIsStream(0)
46 MPD::Song::~Song()
48 if (itsSong)
49 mpd_freeSong(itsSong);
52 void MPD::Song::Clear()
54 if (itsSong)
55 mpd_freeSong(itsSong);
56 itsSong = 0;
57 itsStartTime = 0;
58 itsNoticedPlayback = 0;
59 itsIsStream = 0;
62 void MPD::Song::SetData(mpd_Song *song)
64 if (!song)
65 return;
66 if (itsSong)
67 mpd_freeSong(itsSong);
68 itsSong = song;
69 itsIsStream = strncmp("http://", itsSong->file, 7) == 0;
72 void MPD::Song::SetStartTime()
74 itsStartTime = time(NULL);
77 void MPD::Song::Submit()
79 if (!itsSong)
80 return;
82 if (itsIsStream)
84 itsSong->time = itsNoticedPlayback;
87 pthread_mutex_lock(&handshake_lock);
88 if (canBeSubmitted())
90 if (handshake.status != "OK" || handshake.submission_url.empty())
92 Log(llInfo, "Problems with handshake status, queue song at position %d...", SongsQueue.size());
93 goto SUBMISSION_FAILED;
96 Log(llInfo, "Submitting song...");
98 string result, postdata;
99 CURLcode code;
101 pthread_mutex_lock(&curl_lock);
102 CURL *submission = curl_easy_init();
104 char *c_artist = curl_easy_escape(submission, itsSong->artist, 0);
105 char *c_title = curl_easy_escape(submission, itsSong->title, 0);
106 char *c_album = itsSong->album ? curl_easy_escape(submission, itsSong->album, 0) : NULL;
107 char *c_track = itsSong->track ? curl_easy_escape(submission, itsSong->track, 0) : NULL;
109 postdata = "s=";
110 postdata += handshake.session_id;
111 postdata += "&a[0]=";
112 postdata += c_artist;
113 postdata += "&t[0]=";
114 postdata += c_title;
115 postdata += "&i[0]=";
116 postdata += IntoStr(itsStartTime);
117 postdata += "&o[0]=P";
118 postdata += "&r[0]=";
119 postdata += "&l[0]=";
120 postdata += IntoStr(itsSong->time);
121 postdata += "&b[0]=";
122 if (c_album)
123 postdata += c_album;
124 postdata += "&n[0]=";
125 if (c_track)
126 postdata += c_track;
127 postdata += "&m[0]=";
129 curl_free(c_artist);
130 curl_free(c_title);
131 curl_free(c_album);
132 curl_free(c_track);
134 Log(llVerbose, "URL: %s", handshake.submission_url.c_str());
135 Log(llVerbose, "Post data: %s", postdata.c_str());
137 curl_easy_setopt(submission, CURLOPT_DNS_CACHE_TIMEOUT, 0);
138 curl_easy_setopt(submission, CURLOPT_NOSIGNAL, 1);
139 curl_easy_setopt(submission, CURLOPT_URL, handshake.submission_url.c_str());
140 curl_easy_setopt(submission, CURLOPT_POST, 1);
141 curl_easy_setopt(submission, CURLOPT_POSTFIELDS, postdata.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_timeout);
145 code = curl_easy_perform(submission);
146 curl_easy_cleanup(submission);
147 pthread_mutex_unlock(&curl_lock);
149 ignore_newlines(result);
151 if (result == "OK")
153 Log(llInfo, "Song submitted.");
155 else
157 if (result.empty())
159 Log(llInfo, "Error while submitting song: %s", curl_easy_strerror(code));
161 else
163 Log(llInfo, "Audioscrobbler returned status %s", result.c_str());
165 goto SUBMISSION_FAILED;
168 if (0)
170 SUBMISSION_FAILED: // so we cache not submitted song
172 handshake.Clear(); // handshake probably failed if we are here, so reset it
173 Log(llVerbose, "Handshake status reset");
175 string cache;
176 string offset = IntoStr(SongsQueue.size());
178 char *c_artist = curl_easy_escape(0, itsSong->artist, 0);
179 char *c_title = curl_easy_escape(0, itsSong->title, 0);
180 char *c_album = itsSong->album ? curl_easy_escape(0, itsSong->album, 0) : NULL;
181 char *c_track = itsSong->track ? curl_easy_escape(0, itsSong->track, 0) : NULL;
183 cache = "&a[";
184 cache += offset;
185 cache += "]=";
186 cache += c_artist;
187 cache += "&t[";
188 cache += offset;
189 cache += "]=";
190 cache += c_title;
191 cache += "&i[";
192 cache += offset;
193 cache += "]=";
194 cache += IntoStr(itsStartTime);
195 cache += "&o[";
196 cache += offset;
197 cache += "]=P";
198 cache += "&r[";
199 cache += offset;
200 cache += "]=";
201 cache += "&l[";
202 cache += offset;
203 cache += "]=";
204 cache += IntoStr(itsSong->time);
205 cache += "&b[";
206 cache += offset;
207 cache += "]=";
208 if (c_album)
209 cache += c_album;
210 cache += "&n[";
211 cache += offset;
212 cache += "]=";
213 if (c_track)
214 cache += c_track;
215 cache += "&m[";
216 cache += offset;
217 cache += "]=";
219 Log(llVerbose, "Metadata: %s", cache.c_str());
221 curl_free(c_artist);
222 curl_free(c_title);
223 curl_free(c_album);
224 curl_free(c_track);
226 Cache(cache);
227 SongsQueue.push_back(cache);
228 Log(llInfo, "Song cached.");
230 pthread_mutex_unlock(&handshake_lock);
231 Clear();
234 bool MPD::Song::isStream() const
236 return itsIsStream;
239 int & MPD::Song::Playback()
241 return itsNoticedPlayback;
244 const mpd_Song *& MPD::Song::Data() const
246 return (const mpd_Song *&)itsSong;
249 bool MPD::Song::canBeSubmitted()
251 if (!itsStartTime || itsSong->time < 30 || !itsSong->artist || !itsSong->title)
253 if (!itsStartTime)
255 Log(llInfo, "Song's start time isn't known, not submitting.");
257 else if (itsSong->time < 30)
259 Log(llInfo, "Song's length is too short, not submitting.");
261 else if (!itsSong->artist || !itsSong->title)
263 Log(llInfo, "Song has missing tags, not submitting.");
265 return false;
267 else if (itsNoticedPlayback < 4*60 && itsNoticedPlayback < itsSong->time/2)
269 Log(llInfo, "Noticed playback was too short, not submitting.");
270 return false;
272 return true;