increase tolerance in detecting new songs if songid didn't change
[scrobby.git] / src / song.cpp
blob15868dd083ccf59422e621bbea674e12736a0000
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;
34 extern MPD::Song s;
36 bool MPD::Song::NowPlayingNotify = 0;
38 std::deque<std::string> MPD::Song::SubmitQueue;
39 std::queue<MPD::Song> MPD::Song::Queue;
41 MPD::Song::Song() : Data(0),
42 StartTime(0),
43 Playback(0),
44 itsIsStream(0)
48 MPD::Song::~Song()
50 if (Data)
51 mpd_freeSong(Data);
54 void MPD::Song::Clear()
56 if (Data)
57 mpd_freeSong(Data);
58 Data = 0;
59 StartTime = 0;
60 Playback = 0;
61 itsIsStream = 0;
64 void MPD::Song::SetData(mpd_Song *song)
66 if (!song)
67 return;
68 if (Data)
69 mpd_freeSong(Data);
70 Data = song;
71 itsIsStream = strncmp("http://", Data->file, 7) == 0;
74 void MPD::Song::Submit()
76 if (!Data)
77 return;
79 if (itsIsStream)
80 Data->time = Playback;
82 if (canBeSubmitted())
84 Queue.push(*this);
85 Data = 0;
86 Log(llInfo, "Song queued for submission.");
88 Clear();
91 bool MPD::Song::isStream() const
93 return itsIsStream;
96 bool MPD::Song::canBeSubmitted()
98 if (!StartTime || Data->time < 30 || !Data->artist || !Data->title)
100 if (!StartTime)
102 Log(llInfo, "Song's start time wasn't known, not submitting.");
104 else if (Data->time < 30)
106 Log(llInfo, "Song's length is too short, not submitting.");
108 else if (!Data->artist || !Data->title)
110 Log(llInfo, "Song has missing tags, not submitting.");
112 return false;
114 else if (Playback < 4*60 && Playback < Data->time/2)
116 Log(llInfo, "Noticed playback was too short, not submitting.");
117 return false;
119 return true;
122 void MPD::Song::GetCached()
124 std::ifstream f(Config.file_cache.c_str());
125 if (f.is_open())
127 std::string line;
128 while (!f.eof())
130 getline(f, line);
131 if (!line.empty())
132 SubmitQueue.push_back(line);
137 void MPD::Song::ExtractQueue()
139 for (; !Queue.empty(); Queue.pop())
141 const MPD::Song &s = Queue.front();
143 string cache_str;
144 std::ostringstream cache;
146 char *c_artist = curl_easy_escape(0, s.Data->artist, 0);
147 char *c_title = curl_easy_escape(0, s.Data->title, 0);
148 char *c_album = s.Data->album ? curl_easy_escape(0, s.Data->album, 0) : 0;
149 char *c_track = s.Data->track ? curl_easy_escape(0, s.Data->track, 0) : 0;
150 char *c_mb_trackid = s.Data->musicbrainz_trackid ? curl_easy_escape(0, s.Data->musicbrainz_trackid, 0) : 0;
152 cache
153 << "&a[" << Song::SubmitQueue.size() << "]=" << c_artist
154 << "&t[" << Song::SubmitQueue.size() << "]=" << c_title
155 << "&i[" << Song::SubmitQueue.size() << "]=" << s.StartTime
156 << "&o[" << Song::SubmitQueue.size() << "]=P"
157 << "&r[" << Song::SubmitQueue.size() << "]="
158 << "&l[" << Song::SubmitQueue.size() << "]=" << s.Data->time
159 << "&b[" << Song::SubmitQueue.size() << "]=";
160 if (c_album)
161 cache << c_album;
162 cache << "&n[" << Song::SubmitQueue.size() << "]=";
163 if (c_track)
164 cache << c_track;
165 cache << "&m[" << Song::SubmitQueue.size() << "]=";
166 if (c_mb_trackid)
167 cache << c_mb_trackid;
169 cache_str = cache.str();
171 SubmitQueue.push_back(cache_str);
172 WriteCache(cache_str);
174 curl_free(c_artist);
175 curl_free(c_title);
176 curl_free(c_album);
177 curl_free(c_track);
178 curl_free(c_mb_trackid);
182 bool MPD::Song::SendQueue()
184 ExtractQueue();
186 if (!myHandshake.OK())
187 return false;
189 Log(llInfo, "Submitting songs...");
191 string result, postdata;
192 CURLcode code;
194 postdata = "s=";
195 postdata += myHandshake.SessionID;
197 for (std::deque<string>::const_iterator it = Song::SubmitQueue.begin(); it != Song::SubmitQueue.end(); it++)
198 postdata += *it;
200 Log(llVerbose, "URL: %s", myHandshake.SubmissionURL.c_str());
201 Log(llVerbose, "Post data: %s", postdata.c_str());
203 CURL *submission = curl_easy_init();
204 curl_easy_setopt(submission, CURLOPT_URL, myHandshake.SubmissionURL.c_str());
205 curl_easy_setopt(submission, CURLOPT_POST, 1);
206 curl_easy_setopt(submission, CURLOPT_POSTFIELDS, postdata.c_str());
207 curl_easy_setopt(submission, CURLOPT_WRITEFUNCTION, write_data);
208 curl_easy_setopt(submission, CURLOPT_WRITEDATA, &result);
209 curl_easy_setopt(submission, CURLOPT_CONNECTTIMEOUT, curl_queue_connecttimeout);
210 curl_easy_setopt(submission, CURLOPT_TIMEOUT, curl_queue_timeout);
211 curl_easy_setopt(submission, CURLOPT_DNS_CACHE_TIMEOUT, 0);
212 curl_easy_setopt(submission, CURLOPT_NOPROGRESS, 1);
213 curl_easy_setopt(submission, CURLOPT_NOSIGNAL, 1);
214 code = curl_easy_perform(submission);
215 curl_easy_cleanup(submission);
217 IgnoreNewlines(result);
219 if (result == "OK")
221 Log(llInfo, "Number of submitted songs: %d", Song::SubmitQueue.size());
222 SubmitQueue.clear();
223 std::ofstream f(Config.file_cache.c_str(), std::ios::trunc);
224 f.close();
225 NowPlayingNotify = s.Data && !s.isStream();
226 return true;
228 else
230 if (result.empty())
232 Log(llInfo, "Error while submitting songs: %s", curl_easy_strerror(code));
234 else
236 Log(llInfo, "Audioscrobbler returned status %s", result.c_str());
237 // BADSESSION or FAILED was returned, handshake needs resetting.
238 myHandshake.Clear();
239 Log(llVerbose, "Handshake reset");
241 return false;