1 package org.rpi.plugin.lastfm;
2
3
4
5
6
7
8
9
10 import java.io.File;
11 import java.net.InetSocketAddress;
12 import java.net.Proxy;
13 import java.net.SocketAddress;
14 import java.nio.charset.Charset;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Observable;
18 import java.util.Observer;
19
20 import javax.crypto.Cipher;
21 import javax.crypto.spec.IvParameterSpec;
22 import javax.crypto.spec.SecretKeySpec;
23 import javax.xml.parsers.DocumentBuilder;
24 import javax.xml.parsers.DocumentBuilderFactory;
25 import javax.xml.transform.Transformer;
26 import javax.xml.transform.TransformerFactory;
27 import javax.xml.transform.dom.DOMSource;
28 import javax.xml.transform.stream.StreamResult;
29
30 import net.xeoh.plugins.base.annotations.PluginImplementation;
31
32 import org.apache.log4j.Logger;
33 import org.rpi.os.OSManager;
34 import org.rpi.player.PlayManager;
35 import org.rpi.player.events.EventBase;
36 import org.rpi.player.events.EventTrackChanged;
37 import org.rpi.player.events.EventUpdateTrackMetaText;
38 import org.rpi.playlist.CustomTrack;
39 import org.rpi.utils.Utils;
40 import org.w3c.dom.Document;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.NodeList;
44
45 import de.umass.lastfm.Authenticator;
46 import de.umass.lastfm.Caller;
47 import de.umass.lastfm.Session;
48 import de.umass.lastfm.Track;
49 import de.umass.lastfm.scrobble.ScrobbleData;
50 import de.umass.lastfm.scrobble.ScrobbleResult;
51
52
53
54
55 @PluginImplementation
56 public class LastFmPluginImpl implements LastFmPluginInterface, Observer {
57
58 private static Logger log = Logger.getLogger(LastFmPluginImpl.class);
59
60 private static final String lastfm_api_key = "003dc9a812e87f5058f53439dd26038e";
61 private static final String lastfm_secret = "5a9a78a8442187172d136a84568309f8";
62 private static final String userAgent = "MediaPlayer";
63
64 private String lastfm_username = null;
65 private String lastfm_password = null;
66 private String key = "Ofewtraincrvheg!";
67
68 private Boolean lastfm_debugmode = false;
69
70 private Proxy.Type lastfm_proxymode = Proxy.Type.DIRECT;
71 private String lastfm_proxy_ip = null;
72 private Integer lastfm_proxy_port = null;
73 private String title = "";
74 private String artist = "";
75 private List<BlackList> blackList = new ArrayList<BlackList>();
76
77 private static Session session = null;
78
79
80
81
82 public LastFmPluginImpl() {
83 log.info("Init LastFmPluginImpl");
84 getConfig();
85 init();
86 PlayManager.getInstance().observInfoEvents(this);
87 PlayManager.getInstance().observeProductEvents(this);
88 log.info("Finished LastFmPluginImpl");
89 }
90
91 @Override
92 public void update(Observable o, Object e) {
93 EventBase base = (EventBase) e;
94 switch (base.getType()) {
95 case EVENTTRACKCHANGED:
96 EventTrackChanged etc = (EventTrackChanged) e;
97 CustomTrack track = etc.getTrack();
98 if (track != null) {
99 scrobble(track.getTitle(), track.getPerformer(), track.getAlbum());
100 } else {
101 log.debug("Track was NULL");
102 }
103
104 break;
105 case EVENTUPDATETRACKMETATEXT:
106 EventUpdateTrackMetaText et = (EventUpdateTrackMetaText) e;
107
108 if (et != null)
109 scrobble(et.getTitle(), et.getArtist());
110 break;
111 }
112 }
113
114
115
116
117
118
119
120 private void scrobble(String title, String artist) {
121 scrobble(title, artist, "");
122 }
123
124
125
126
127
128
129
130
131 private void scrobble(String title, String artist, String album) {
132 if(session == null)
133 return;
134 if (!changedTrack(title, artist))
135 return;
136 if (title.equalsIgnoreCase("") || artist.equalsIgnoreCase("")) {
137 log.debug("One is a blank Title: " + title + " Artist: " + artist);
138 return;
139 }
140
141 for (BlackList bl : blackList) {
142 if (bl.matches(artist, title)) {
143 log.debug("BlackList Found Title: " + title + " : Artist: " + artist + "Rule: " + bl.toString());
144 return;
145 }
146 }
147
148 log.debug("TrackChanged: " + title + " : " + artist + " Album: " + album);
149 int now = (int) (System.currentTimeMillis() / 1000);
150 ScrobbleData data = new ScrobbleData();
151 data.setTimestamp(now);
152 data.setArtist(artist);
153 data.setTrack(title);
154 if (!Utils.isEmpty(album)) {
155 data.setAlbum(album);
156 }
157 ScrobbleResult sres = Track.scrobble(data, session);
158 if (!sres.isSuccessful()|| sres.isIgnored())
159 {
160 log.debug(sres.toString());
161 }
162 }
163
164
165
166
167
168 private void init() {
169 try {
170 log.debug("INIT");
171 Caller.getInstance().setUserAgent(userAgent);
172
173 if (lastfm_proxymode != Proxy.Type.DIRECT) {
174 SocketAddress sa = new InetSocketAddress(lastfm_proxy_ip, lastfm_proxy_port);
175 Proxy proxy = new Proxy(lastfm_proxymode, sa);
176 Caller.getInstance().setProxy(proxy);
177 }
178
179 if (lastfm_username.equalsIgnoreCase("") || lastfm_password == null || lastfm_password.equalsIgnoreCase("")) {
180 log.error("LastFM User Credentials not supplied");
181 } else {
182 session = Authenticator.getMobileSession(lastfm_username, lastfm_password, lastfm_api_key, lastfm_secret);
183 log.debug("SessionKey: " + session.getKey());
184 }
185 } catch (Exception e) {
186 log.error(e);
187 }
188 log.debug("End of INIT");
189 }
190
191
192
193
194 private void getConfig() {
195 try {
196 String class_name = this.getClass().getName();
197 log.debug("Find Class, ClassName: " + class_name);
198 String path = OSManager.getInstance().getFilePath(this.getClass(), false);
199 log.debug("Getting LastFM.xml from Directory: " + path);
200 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
201 DocumentBuilder builder = factory.newDocumentBuilder();
202 File file = new File(path + "LastFM.xml");
203 Document doc = builder.parse(file);
204 NodeList listOfConfig = doc.getElementsByTagName("Config");
205 int i = 1;
206 String encrypted_password = "";
207 for (int s = 0; s < listOfConfig.getLength(); s++) {
208 Node config = listOfConfig.item(s);
209 if (config.getNodeType() == Node.ELEMENT_NODE) {
210 Element element = (Element) config;
211 lastfm_username = getElementTest(element, "UserName", "");
212 String password = "";
213 password = getElementTest(element, "Password", "");
214 if (!password.equalsIgnoreCase("")) {
215 encrypted_password = encrypt(key, password);
216 lastfm_password = password;
217 } else {
218 String enc_password = getElementTest(element, "Password_ENC", "");
219 if (!enc_password.equalsIgnoreCase("")) {
220 lastfm_password = decrypt(key, enc_password);
221 }
222 }
223 String proxymode = getElementTest(element, "ProxyType", "DIRECT");
224 lastfm_proxymode = Proxy.Type.valueOf(proxymode);
225 lastfm_proxy_ip = getElementTest(element, "Proxy_IP", "");
226 String proxy_port = getElementTest(element, "Proxy_Port", "-1");
227 lastfm_proxy_port = Integer.parseInt(proxy_port);
228 }
229 }
230
231 NodeList listOfBlackList = doc.getElementsByTagName("BlackListItem");
232 for (int s = 0; s < listOfBlackList.getLength(); s++) {
233 Node bl = listOfBlackList.item(s);
234 if (bl.getNodeType() == Node.ELEMENT_NODE) {
235 Element element = (Element) bl;
236 String artist = getElementTest(element, "artist", "");
237 String title = getElementTest(element, "title", "");
238 BlackList bli = new BlackList();
239 bli.setArtist(artist);
240 bli.setTitle(title);
241 log.debug("Adding BlackList: " + bli.toString());
242 blackList.add(bli);
243 }
244 }
245
246 if (!encrypted_password.equalsIgnoreCase("")) {
247 updateMyXML(doc, "LastFM/Config/Password", "");
248 updateMyXML(doc, "LastFM/Config/Password_ENC", encrypted_password);
249 TransformerFactory transformerFactory = TransformerFactory.newInstance();
250 Transformer transformer = transformerFactory.newTransformer();
251 transformer.transform(new DOMSource(doc), new StreamResult(file));
252
253 }
254
255 } catch (Exception e) {
256 log.error("Error Reading LastFM.xml", e);
257 }
258 }
259
260
261
262
263
264
265
266 public void updateMyXML(Document doc, String path, String def) {
267 String p[] = path.split("/");
268
269 Node n = doc;
270 for (int i = 0; i < p.length; i++) {
271 NodeList kids = n.getChildNodes();
272 Node nfound = null;
273 for (int j = 0; j < kids.getLength(); j++)
274 if (kids.item(j).getNodeName().equals(p[i])) {
275 nfound = kids.item(j);
276 break;
277 }
278 if (nfound == null) {
279 nfound = doc.createElement(p[i]);
280 n.appendChild(nfound);
281 n.appendChild(doc.createTextNode("\n"));
282 }
283 n = nfound;
284 }
285 NodeList kids = n.getChildNodes();
286 for (int i = 0; i < kids.getLength(); i++)
287 if (kids.item(i).getNodeType() == Node.TEXT_NODE) {
288
289 kids.item(i).setNodeValue(def);
290 return;
291 }
292 n.appendChild(doc.createTextNode(def));
293 }
294
295
296
297
298
299
300
301 private boolean changedTrack(String title, String artist) {
302 if (this.title.equalsIgnoreCase(title) && this.artist.equalsIgnoreCase(artist)) {
303 log.debug("Track didn't Change: " + title + " : " + artist);
304 return false;
305 }
306
307 this.title = title;
308 this.artist = artist;
309 return true;
310 }
311
312
313
314
315
316
317
318 private String getElementTest(Element element, String name, String default_value) {
319 String res = default_value;
320 NodeList nid = element.getElementsByTagName(name);
321 if (nid != null) {
322 Element fid = (Element) nid.item(0);
323 if (fid != null) {
324 res = fid.getTextContent();
325 if (res.equalsIgnoreCase(""))
326 res = default_value;
327 return res;
328 }
329 }
330 return res;
331 }
332
333
334 private String encrypt(String key, String value) {
335 try {
336 byte[] raw = key.getBytes(Charset.forName("UTF-8"));
337 SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
338 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
339 cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
340 byte[] encrypted = cipher.doFinal(value.getBytes());
341 return Base64.encode(encrypted);
342 } catch (Exception ex) {
343 log.error("Error encrypt: " ,ex);
344 }
345 return null;
346 }
347
348 private String decrypt(String key, String encrypted) {
349 try {
350 SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
351 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
352 cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
353 byte[] original = cipher.doFinal(Base64.decode(encrypted));
354
355 return new String(original);
356 } catch (Exception ex) {
357 log.error("Error decrypt: " ,ex);
358 }
359 return null;
360 }
361 }