poll mpd once per second, there is no need to do it twice
[scrobby.git] / src / scrobby.cpp
blobe866c04c1591486593f97bb42679018e4f3c82b3
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 <csignal>
22 #include <cstdlib>
23 #include <curl/curl.h>
24 #include <iostream>
26 #include "callback.h"
27 #include "configuration.h"
28 #include "misc.h"
29 #include "scrobby.h"
30 #include "song.h"
31 #include "mpdpp.h"
33 using std::string;
35 Handshake myHandshake;
36 MPD::Song s;
38 namespace
40 void do_at_exit();
41 void signal_handler(int);
43 bool handshake_sent_properly();
46 int main(int argc, char **argv)
48 DefaultConfiguration(Config);
50 if (argc > 1)
52 ParseArgv(Config, argc, argv);
54 if (!Config.file_config.empty())
56 if (!ReadConfiguration(Config, Config.file_config))
58 std::cerr << "cannot read configuration file: " << Config.file_config << std::endl;
59 return 1;
62 else if (!ReadConfiguration(Config, Config.user_home_folder + "/.scrobbyconf"))
64 if (!ReadConfiguration(Config, "/etc/scrobby.conf"))
66 std::cerr << "default configuration files not found!\n";
67 return 1;
70 if (Config.log_level == llUndefined)
72 Config.log_level = llInfo;
74 if (Config.lastfm_user.empty() || (Config.lastfm_md5_password.empty() && Config.lastfm_password.empty()))
76 std::cerr << "last.fm user/password is not set.\n";
77 return 1;
79 ChangeToUser();
80 if (!CheckFiles(Config))
82 return 1;
84 if (Config.daemonize)
86 if (!Daemonize())
87 std::cerr << "couldn't daemonize!\n";
90 MPD::Song::GetCached();
92 MPD::Connection *Mpd = new MPD::Connection;
94 if (Config.mpd_host != "localhost")
95 Mpd->SetHostname(Config.mpd_host);
96 if (Config.mpd_port != 6600)
97 Mpd->SetPort(Config.mpd_port);
99 Mpd->SetTimeout(Config.mpd_timeout);
100 Mpd->SetStatusUpdater(ScrobbyStatusChanged, NULL);
101 Mpd->SetErrorHandler(ScrobbyErrorCallback, NULL);
103 signal(SIGHUP, signal_handler);
104 signal(SIGINT, signal_handler);
105 signal(SIGTERM, signal_handler);
106 signal(SIGPIPE, SIG_IGN);
108 atexit(do_at_exit);
110 int handshake_delay = 0;
111 int mpd_delay = 0;
113 time_t now = 0;
114 time_t handshake_ts = 0;
115 time_t mpd_ts = 0;
117 while (!sleep(1))
119 time(&now);
120 if (now > handshake_ts && myHandshake.Status != "OK")
122 myHandshake.Clear();
123 if (handshake_sent_properly() && !myHandshake.Status.empty())
125 Log(llInfo, "Handshake returned %s", myHandshake.Status.c_str());
127 if (myHandshake.Status == "OK")
129 Log(llInfo, "Connected to Audioscrobbler!");
130 handshake_delay = 0;
132 else
134 handshake_delay += 10;
135 Log(llInfo, "Connection to Audioscrobbler refused, retrieving in %d seconds...", handshake_delay);
136 handshake_ts = time(0)+handshake_delay;
139 if (Mpd->Connected())
141 Mpd->UpdateStatus();
143 else if (now > mpd_ts)
145 s.Submit();
146 Log(llVerbose, "Connecting to MPD...");
147 if (Mpd->Connect())
149 Log(llInfo, "Connected to MPD at %s !", Config.mpd_host.c_str());
150 mpd_delay = 0;
152 else
154 mpd_delay += 10;
155 Log(llInfo, "Cannot connect to MPD, retrieving in %d seconds...", mpd_delay);
156 mpd_ts = time(0)+mpd_delay;
160 return 0;
163 namespace
165 void do_at_exit()
167 s.Submit();
168 Log(llInfo, "Shutting down...");
169 if (remove(Config.file_pid.c_str()) != 0)
170 Log(llInfo, "Couldn't remove pid file!");
173 void signal_handler(int)
175 exit(0);
178 bool handshake_sent_properly()
180 CURLcode code;
181 string handshake_url;
182 string result;
183 string timestamp = IntoStr(time(NULL));
185 handshake_url = "http://post.audioscrobbler.com/?hs=true&p=1.2.1&c=mpc&v="VERSION"&u=";
186 handshake_url += Config.lastfm_user;
187 handshake_url += "&t=";
188 handshake_url += timestamp;
189 handshake_url += "&a=";
190 handshake_url += md5sum((Config.lastfm_md5_password.empty() ? md5sum(Config.lastfm_password) : Config.lastfm_md5_password) + timestamp);
192 CURL *hs = curl_easy_init();
193 curl_easy_setopt(hs, CURLOPT_URL, handshake_url.c_str());
194 curl_easy_setopt(hs, CURLOPT_WRITEFUNCTION, write_data);
195 curl_easy_setopt(hs, CURLOPT_WRITEDATA, &result);
196 curl_easy_setopt(hs, CURLOPT_CONNECTTIMEOUT, curl_connecttimeout);
197 curl_easy_setopt(hs, CURLOPT_TIMEOUT, curl_timeout);
198 curl_easy_setopt(hs, CURLOPT_DNS_CACHE_TIMEOUT, 0);
199 curl_easy_setopt(hs, CURLOPT_NOPROGRESS, 1);
200 curl_easy_setopt(hs, CURLOPT_NOSIGNAL, 1);
201 code = curl_easy_perform(hs);
202 curl_easy_cleanup(hs);
204 if (code != CURLE_OK)
206 Log(llInfo, "Error while sending handshake: %s", curl_easy_strerror(code));
207 return false;
210 size_t i = result.find("\n");
211 myHandshake.Status = result.substr(0, i);
212 if (myHandshake.Status != "OK")
213 return false;
214 result = result.substr(i+1);
215 i = result.find("\n");
216 myHandshake.SessionID = result.substr(0, i);
217 result = result.substr(i+1);
218 i = result.find("\n");
219 myHandshake.NowPlayingURL = result.substr(0, i);
220 result = result.substr(i+1);
221 IgnoreNewlines(result);
222 myHandshake.SubmissionURL = result;
223 return true;