1 /***************************************************************************
2 * Copyright (C) 2008 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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. *
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. *
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 ***************************************************************************/
22 #include <curl/curl.h>
28 #include "configuration.h"
41 pthread_t mpdconnection_th
;
42 pthread_t handshake_th
;
44 pthread_mutex_t curl_lock
= PTHREAD_MUTEX_INITIALIZER
;
45 pthread_mutex_t handshake_lock
= PTHREAD_MUTEX_INITIALIZER
;
47 std::vector
<string
> SongsQueue
;
49 bool scrobby_exit
= 0;
50 bool notify_about_now_playing
= 0;
54 void signal_handler(int);
55 bool send_handshake();
57 void *mpdconnection_handler(void *data
);
58 void *handshake_handler(void *);
61 int main(int argc
, char **argv
)
63 const string config_file
= argc
> 1 ? argv
[1] : "/etc/scrobby.conf";
65 DefaultConfiguration(config
);
67 if (!ReadConfiguration(config
, config_file
))
69 std::cerr
<< "cannot read configuration file: " << config_file
<< std::endl
;
72 if (config
.lastfm_user
.empty() || (config
.lastfm_md5_password
.empty() && config
.lastfm_password
.empty()))
74 std::cerr
<< "last.fm user/password is not set.\n";
77 if (!CheckFiles(config
))
82 std::cerr
<< "couldn't daemonize!\n";
84 GetCachedSongs(SongsQueue
);
86 MPD::Connection
*Mpd
= new MPD::Connection
;
88 if (config
.mpd_host
!= "localhost")
89 Mpd
->SetHostname(config
.mpd_host
);
90 if (config
.mpd_port
!= 6600)
91 Mpd
->SetPort(config
.mpd_port
);
92 if (!config
.mpd_password
.empty())
93 Mpd
->SetPassword(config
.mpd_password
);
95 Mpd
->SetTimeout(config
.mpd_timeout
);
96 Mpd
->SetStatusUpdater(ScrobbyStatusChanged
, NULL
);
97 Mpd
->SetErrorHandler(ScrobbyErrorCallback
, NULL
);
99 signal(SIGHUP
, signal_handler
);
100 signal(SIGINT
, signal_handler
);
101 signal(SIGTERM
, signal_handler
);
102 signal(SIGPIPE
, SIG_IGN
);
104 pthread_create(&mpdconnection_th
, NULL
, mpdconnection_handler
, Mpd
);
105 pthread_create(&handshake_th
, NULL
, handshake_handler
, NULL
);
106 pthread_detach(mpdconnection_th
);
107 pthread_detach(handshake_th
);
109 while (!scrobby_exit
&& !usleep(500000))
111 if (Mpd
->Connected())
116 Log("Shutting down...", llInfo
);
117 if (remove(config
.file_pid
.c_str()) != 0)
118 Log("Couldn't remove pid file!", llInfo
);
126 void signal_handler(int)
131 bool send_handshake()
134 string handshake_url
;
136 string timestamp
= IntoStr(time(NULL
));
138 handshake_url
= "http://post.audioscrobbler.com/?hs=true&p=1.2.1&c=mpc&v="VERSION
"&u=";
139 handshake_url
+= config
.lastfm_user
;
140 handshake_url
+= "&t=";
141 handshake_url
+= timestamp
;
142 handshake_url
+= "&a=";
143 handshake_url
+= md5sum((config
.lastfm_md5_password
.empty() ? md5sum(config
.lastfm_password
) : config
.lastfm_md5_password
) + timestamp
);
145 pthread_mutex_lock(&curl_lock
);
146 CURL
*hs
= curl_easy_init();
147 curl_easy_setopt(hs
, CURLOPT_URL
, handshake_url
.c_str());
148 curl_easy_setopt(hs
, CURLOPT_WRITEFUNCTION
, write_data
);
149 curl_easy_setopt(hs
, CURLOPT_WRITEDATA
, &result
);
150 curl_easy_setopt(hs
, CURLOPT_CONNECTTIMEOUT
, curl_timeout
);
151 code
= curl_easy_perform(hs
);
152 curl_easy_cleanup(hs
);
153 pthread_mutex_unlock(&curl_lock
);
155 if (code
!= CURLE_OK
)
157 Log("Error while sending handshake: " + string(curl_easy_strerror(code
)), llInfo
);
161 size_t i
= result
.find("\n");
162 handshake
.status
= result
.substr(0, i
);
163 if (handshake
.status
!= "OK")
165 result
= result
.substr(i
+1);
166 i
= result
.find("\n");
167 handshake
.session_id
= result
.substr(0, i
);
168 result
= result
.substr(i
+1);
169 i
= result
.find("\n");
170 handshake
.nowplaying_url
= result
.substr(0, i
);
171 result
= result
.substr(i
+1);
172 ignore_newlines(result
);
173 handshake
.submission_url
= result
;
177 void *mpdconnection_handler(void *data
)
179 MPD::Connection
*Mpd
= static_cast<MPD::Connection
*>(data
);
180 while (!scrobby_exit
)
183 while (!Mpd
->Connected())
186 Log("Connecting to MPD...", llVerbose
);
190 Log("Connected to " + config
.mpd_host
+ "!", llInfo
);
196 Log("Cannot connect, retrieving in " + IntoStr(10*x
) + " seconds...", llInfo
);
205 void *handshake_handler(void *)
208 while (!scrobby_exit
)
210 if (handshake
.status
!= "OK")
212 pthread_mutex_lock(&handshake_lock
);
214 if (send_handshake() && !handshake
.status
.empty())
216 Log("Handshake returned " + handshake
.status
, llInfo
);
218 if (handshake
.status
== "OK")
220 Log("Connected to Audioscrobbler!", llInfo
);
221 if (!SongsQueue
.empty())
223 Log("Queue's not empty, submitting songs...", llInfo
);
225 string result
, postdata
;
228 pthread_mutex_lock(&curl_lock
);
229 CURL
*submission
= curl_easy_init();
232 postdata
+= handshake
.session_id
;
234 for (std::vector
<string
>::const_iterator it
= SongsQueue
.begin(); it
!= SongsQueue
.end(); it
++)
237 Log("URL: " + handshake
.submission_url
, llVerbose
);
238 Log("Post data: " + postdata
, llVerbose
);
240 curl_easy_setopt(submission
, CURLOPT_URL
, handshake
.submission_url
.c_str());
241 curl_easy_setopt(submission
, CURLOPT_POST
, 1);
242 curl_easy_setopt(submission
, CURLOPT_POSTFIELDS
, postdata
.c_str());
243 curl_easy_setopt(submission
, CURLOPT_WRITEFUNCTION
, write_data
);
244 curl_easy_setopt(submission
, CURLOPT_WRITEDATA
, &result
);
245 curl_easy_setopt(submission
, CURLOPT_CONNECTTIMEOUT
, curl_timeout
);
246 code
= curl_easy_perform(submission
);
247 curl_easy_cleanup(submission
);
248 pthread_mutex_unlock(&curl_lock
);
250 ignore_newlines(result
);
254 Log("Number of submitted songs: " + IntoStr(SongsQueue
.size()), llInfo
);
263 Log("Error while submitting songs: " + string(curl_easy_strerror(code
)), llInfo
);
267 Log("Audioscrobbler returned status " + result
, llInfo
);
271 notify_about_now_playing
= !s
.isStream();
276 Log("Connection refused, retrieving in " + IntoStr(10*x
) + " seconds...", llInfo
);
279 pthread_mutex_unlock(&handshake_lock
);