Ticket #2: Scrobbler.java

File Scrobbler.java, 9.6 kB (added by morgan_guerin@yahoo.fr, 3 years ago)

Scrobbler modified class

Line 
1 /*
2  * LastPod is an application used to publish one's iPod play counts to Last.fm.
3  * Copyright (C) 2007  muti, Chris Tilden
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (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 Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  * package org.lastpod;
19  */
20 package org.lastpod;
21
22 import java.io.BufferedReader;
23 import java.io.IOException;
24 import java.io.InputStreamReader;
25 import java.io.OutputStreamWriter;
26 import java.io.UnsupportedEncodingException;
27
28 import java.net.HttpURLConnection;
29 import java.net.MalformedURLException;
30 import java.net.URL;
31 import java.net.URLEncoder;
32
33 import java.security.MessageDigest;
34 import java.security.NoSuchAlgorithmException;
35
36 import java.text.SimpleDateFormat;
37
38 import java.util.Date;
39 import java.util.List;
40 import java.util.TimeZone;
41 import java.util.logging.Level;
42 import java.util.logging.Logger;
43 import java.util.regex.Matcher;
44 import java.util.regex.Pattern;
45
46 import javax.security.auth.login.FailedLoginException;
47
48 /**
49  * @author muti
50  * @version $Id$
51  */
52 public class Scrobbler {
53     private String username;
54     private String encryptedPassword;
55     private String challenge;
56     private String submithost;
57     private Integer submitport;
58     private String submiturl;
59     private Logger logger;
60
61     public Scrobbler(String username, String encryptedPassword) {
62         this.username = username;
63         this.encryptedPassword = encryptedPassword;
64         this.logger = Logger.getLogger(this.getClass().getPackage().getName());
65     }
66
67     public void handshake(List recentplayed)
68             throws UnsupportedEncodingException, MalformedURLException, IOException,
69                 FailedLoginException {
70         if (recentplayed.size() == 0) {
71             throw new RuntimeException("No tracks to submit");
72         }
73
74         this.logger.log(Level.INFO, "Beginning Handshake");
75
76         String args = "?hs=true&p=1.1&c=apd&v=0.1&u=" + URLEncoder.encode(this.username, "UTF-8");
77         URL url = new URL("http://post.audioscrobbler.com/" + args);
78         this.logger.log(Level.FINE, "Handshaking to URL: " + url.toString());
79
80         HttpURLConnection c = (HttpURLConnection) url.openConnection();
81
82         c.setDoInput(true);
83         c.setRequestMethod("GET");
84         c.setUseCaches(false);
85         c.setRequestProperty("Connection", "close");
86         c.connect();
87
88         if (c.getResponseCode() != 200) {
89             throw new RuntimeException("Invalid HTTP return code");
90         }
91
92         BufferedReader breader = new BufferedReader(new InputStreamReader(c.getInputStream()));
93
94         String content = null;
95         String buffer = null;
96
97         while ((buffer = breader.readLine()) != null) {
98             if (content != null) {
99                 content += (buffer + "\n");
100             } else {
101                 content = buffer + "\n";
102             }
103         }
104
105         this.logger.log(Level.FINE, "Received from server:\n" + content);
106
107         if ((content == null) || (content.length() == 0)) {
108             throw new RuntimeException("Invalid response received from AudioScrobbler");
109         }
110
111         String[] lines = content.split("\n");
112
113         if ((lines[0].length() >= 6) && lines[0].substring(0, 6).equals("FAILED")) {
114             throw new RuntimeException(lines[0].substring(7));
115         }
116
117         if ((lines[0].length() >= 7) && lines[0].substring(0, 7).equals("BADUSER")) {
118             throw new FailedLoginException("Invalid Username");
119         }
120
121         if ((lines[0].length() >= 6) && lines[0].substring(0, 6).equals("UPDATE")) {
122             throw new RuntimeException("Update your client:" + lines[0].substring(7));
123         }
124
125         Pattern p = Pattern.compile("http://(.*):(\\d+)(.*)");
126         Matcher m = p.matcher(lines[2]);
127
128         if (m.matches()) {
129             this.submithost = m.group(1);
130             this.submitport = new Integer(m.group(2));
131             this.submiturl = m.group(3);
132             this.logger.log(Level.FINE, "Set submithost to: " + this.submithost);
133             this.logger.log(Level.FINE, "Set submitport to: " + this.submitport);
134             this.logger.log(Level.FINE, "Set submiturl to: " + this.submiturl);
135         } else {
136             throw new RuntimeException("Invalid POST URL returned, unable to continue");
137         }
138
139         this.challenge = lines[1];
140
141         this.logger.log(Level.INFO, "Handshake completed");
142     }
143
144     public void submittracks(List recentplayed)
145             throws UnsupportedEncodingException, NoSuchAlgorithmException, MalformedURLException,
146                 IOException, FailedLoginException {
147         this.logger.log(Level.INFO, "Submitting tracks...");
148
149         if (recentplayed.size() == 0) {
150             throw new RuntimeException("No tracks to submit");
151         }
152
153         MessageDigest md = MessageDigest.getInstance("MD5");
154         String md5pass = encryptedPassword + this.challenge;
155         String md5chal = MiscUtilities.hexEncode(md.digest(md5pass.getBytes()));
156
157         String querystring =
158             "u=" + URLEncoder.encode(this.username) + "&" + "s=" + URLEncoder.encode(md5chal) + "&";
159
160         int tracknum = 0;
161
162         for (int i = 0; i < recentplayed.size(); i++) {
163             TrackItem track = (TrackItem) recentplayed.get(i);
164
165             if (track.getLength() < 30) {
166                 continue;
167             }
168
169             String artistutf8 = new String(track.getArtist().getBytes("UTF-8"), "UTF-8");
170             String trackutf8 = new String(track.getTrack().getBytes("UTF-8"), "UTF-8");
171             String albumutf8 = new String(track.getAlbum().getBytes("UTF-8"), "UTF-8");
172             Date date = new Date(track.getLastplayed() * 1000);
173             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
174             format.setTimeZone(TimeZone.getTimeZone("GMT:00"));
175
176             String datestring = format.format(date);
177
178             querystring += ("a[" + tracknum + "]=" + URLEncoder.encode(artistutf8, "UTF-8") + "&");
179             querystring += ("t[" + tracknum + "]=" + URLEncoder.encode(trackutf8, "UTF-8") + "&");
180             querystring += ("b[" + tracknum + "]=" + URLEncoder.encode(albumutf8, "UTF-8") + "&");
181             querystring += ("m[" + tracknum + "]=" + "&");
182             querystring += ("l[" + tracknum + "]="
183             + URLEncoder.encode(new Long(track.getLength()).toString(), "UTF-8") + "&");
184             querystring += ("i[" + tracknum + "]=" + URLEncoder.encode(datestring, "UTF-8") + "&");
185
186             tracknum++;
187         }
188
189         querystring = querystring.substring(0, querystring.length() - 1); //trim last &
190
191         URL url = new URL("http://" + this.submithost + ":" + this.submitport + this.submiturl);
192         this.logger.log(Level.FINE, "Submitting tracks to URL: " + url.toString());
193
194         HttpURLConnection c = (HttpURLConnection) url.openConnection();
195         c.setRequestMethod("POST");
196         c.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
197         c.setRequestProperty("Content-Length", new Integer(querystring.length()).toString());
198         c.setRequestProperty("Connection", "close");
199         c.setDoInput(true);
200         c.setDoOutput(true);
201         c.setUseCaches(false);
202         c.connect();
203
204         this.logger.log(Level.FINE, "POST query string:\n" + querystring);
205
206         OutputStreamWriter wr = new OutputStreamWriter(c.getOutputStream());
207         wr.write(querystring);
208         wr.flush();
209         wr.close();
210
211         if (c.getResponseCode() != 200) {
212             throw new RuntimeException("Invalid HTTP return code");
213         }
214
215         BufferedReader breader = new BufferedReader(new InputStreamReader(c.getInputStream()));
216
217         String content = null;
218         String buffer = null;
219
220         while ((buffer = breader.readLine()) != null) {
221             if (content != null) {
222                 content += (buffer + "\n");
223             } else {
224                 content = buffer + "\n";
225             }
226         }
227
228         this.logger.log(Level.FINE, "Received from server:\n" + content);
229
230         if ((content == null) || (content.length() == 0)) {
231             throw new RuntimeException("Invalid response received from AudioScrobbler");
232         }
233
234         String[] lines = content.split("\n");
235
236         if ((lines[0].length() >= 6) && lines[0].substring(0, 6).equals("FAILED")) {
237             throw new RuntimeException(lines[0].substring(7));
238         }
239
240         if ((lines[0].length() >= 7) && lines[0].substring(0, 7).equals("BADAUTH")) {
241             throw new FailedLoginException("Invalid username/password");
242         }
243
244         if ((lines[0].length() >= 2) && !lines[0].substring(0, 2).equals("OK")) {
245             throw new RuntimeException("Unknown error submitting tracks");
246         }
247
248         this.logger.log(Level.INFO, "Tracks submitted");
249         this.logger.log(Level.INFO,
250             "You must now sync your iPod with your music management software "
251             + "or delete 'Play Counts' from the iTunes folder!");
252        
253        
254         for (int i = 0; i < recentplayed.size(); i++) {
255             TrackItem track = (TrackItem) recentplayed.get(i);           
256             History.getInstance().addhistory(track.getLastplayed());           
257         }
258        
259         History.getInstance().write();
260     }
261 }