Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/tuneology/avm/FreeDb.java


1   /*
2     FreeDb.java
3   
4     Copyright (C) 2002 Fran Taylor
5   
6     This file is part of java-avm.
7   
8     java-avm is free software; you can redistribute it and/or modify it
9     under the terms of the GNU Lesser General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12  
13    java-avm is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17  
18    You should have received a copy of the GNU Lesser General Public
19    License along with java-avm; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21    USA
22  
23    $Id: FreeDb.java,v 1.4 2002/11/02 19:27:45 xnarf Exp $
24  
25  */
26  
27  package com.tuneology.avm;
28  
29  import java.io.*;
30  import java.net.*;
31  import java.util.*;
32  
33  /**
34   * An API for the FreeDb song database. 
35   * <p>
36   * This API is single threaded; if you want to use it in a multithreaded program, 
37   * create an instance of the FreeDb object for each thread.
38   * 
39   * @author Fran Taylor
40   *
41   * @version $Id: FreeDb.java,v 1.4 2002/11/02 19:27:45 xnarf Exp $
42   */
43  
44  public class FreeDb {
45      /**
46       * A class to represent a FreeDb server site.
47       */
48      public static class Site {
49          /** The hostname of the server. */
50    public String host;
51          /** The description of the the server's location. */
52    public String location;
53          /** The server's latitude and longitude. */
54    public String position;
55      }
56      /** Create a FreeDb object, ready to make queries.
57       *
58       * @param host The hostname of the freedb server.
59       * @param appName The name of the application.
60       * @param version The version of the application.
61       * @param url The URL to use for the request. If null, uses "/~cddb/cddb.cgi".
62       * @param proxyHost The http proxy host (optional).
63       * @param proxyPort The http proxy port number.
64       * @param cacheDir A directory to use for storing cached files (optional).
65       */
66      public FreeDb(String host, String appName, String version, String url, String proxyHost, int proxyPort, File cacheDir) {
67    if (host == null) host = "freedb.freedb.org";
68    
69    this.urlString = "http://" + host + ((url == null) ? "/~cddb/cddb.cgi" : url);
70    this.proxyHost = proxyHost;
71    this.proxyPort = proxyPort;
72    this.cacheDir = cacheDir;
73    String hostName;
74    try {
75        hostName = InetAddress.getLocalHost().getHostName();
76    } catch (Exception e) {
77        hostName = "localhost";
78    }
79    idStr = "&hello=" + 
80        System.getProperty("user.name") + "+" + hostName + "+" + appName + "+" + version + "&proto=5";
81      }
82      /** 
83       * Translated a discid value from an integer to an 8-digit hex number .
84       *
85       * @param discId the integer value.
86       * @return a hex string representing the value.
87       */
88      public static String getHexId(int discId) {
89          String retval = Long.toHexString(((long) discId) & 0xffffffffL);
90           for(int i = retval.length(); i < 8; i++) retval = "0" + retval;
91           return retval;
92      }
93      /** 
94       * Returns a list of the possible entries for the disc. 
95       * The user chooses one and then get() is called to fetch 
96       * the cddb data.
97       *
98       * @param trackStr The list of tracks, formatted as required for freedb.
99       * @return The list of category entries for the disc.
100      * @throws IOException if there is an error reading from the network.
101      */
102     public String []getCateg(String trackStr) throws IOException {
103   // look up the disc in the cache first
104   if (cacheDir != null) {
105       
106   }
107   // not there, now try the FreeDb database
108   String req = urlString + "?cmd=cddb+query+" + trackStr + idStr;
109   String[] retval = null;
110   URL url;
111   BufferedReader urlRdr = null;
112   logOutput = "request: \"" + req + "\"\n";
113   url = new URL(req);
114   urlRdr = new BufferedReader(new InputStreamReader(url.openStream()));
115   String str = urlRdr.readLine();
116   int code = Integer.parseInt(str.substring(0, 3));
117   logOutput += "response: \"" + str + "\"\n";
118   if (code == 200) {
119       retval = new String[1];
120       retval[0] = str.substring(4);
121   } else if ((code == 210) || (code == 211)) {
122       Vector v = new Vector();
123       while((str = urlRdr.readLine()) != null) {
124     logOutput += "response: \"" + str + "\"\n";
125     if (!str.equals(".")) 
126         v.add(str);
127       }
128       retval = new String[v.size()];
129       for(int i = 0; i < v.size(); i++) { retval[i] = (String) v.get(i); }
130   } else 
131       throw new IOException(str);
132   urlRdr.close();
133   return retval;
134     }
135     
136     /** 
137      * Returns a list of available servers.
138      * 
139      * @return An array containing the available servers.
140      * @throws IOException if there is an error reading from the network.
141      */
142     public Site[] getServers() throws IOException {
143   setProxy();
144   URL url = new URL(urlString + "?cmd=sites" + idStr);
145   BufferedReader urlRdr = new BufferedReader(new InputStreamReader(url.openStream()));
146   String str = urlRdr.readLine();
147   resetProxy();
148   int code = Integer.parseInt(str.substring(0, 3));
149   if (code != 210) {
150       throw new IOException(str);
151   }
152   Vector hosts = new Vector();
153   Vector locs = new Vector();
154   Vector poss = new Vector();
155   while((str = urlRdr.readLine()) != null) {
156       if (str.equals(".")) continue;
157       StringTokenizer tok = new StringTokenizer(str, " ");
158       String host = tok.nextToken();
159       String proto = tok.nextToken();
160       String port = tok.nextToken();
161       String req = tok.nextToken();
162       String lat = tok.nextToken();
163       String lng = tok.nextToken();
164       String loc = tok.nextToken("\n").trim();
165       if (proto.equals("http") && port.equals("80")) {
166     hosts.add(host);
167     locs.add(loc);
168     poss.add(lat + ", " + lng);
169       }
170   }
171   urlRdr.close();
172   Site[] retval = new Site[hosts.size()];
173   for(int i = 0; i < hosts.size(); i++) {
174       retval[i] = new Site();
175       retval[i].host = (String) hosts.get(i);
176       retval[i].location = (String) locs.get(i);
177       retval[i].position = (String) poss.get(i);
178   }
179   return retval;
180     }
181 
182     /**
183      * Returns information about the album, given 
184      * its discid key, 
185      * 
186      * @param categ the category string.
187      * @param ent the entry to fill in.
188      * @return true if the information was found.
189      * @throws IOException if there is an error reading from the network.
190      */
191     public boolean get(String categStr, DiscInfo ent) throws IOException {
192   String discId = getHexId(ent.discId);
193   stuff = "";
194   // try looking in the cache directory first
195         StringTokenizer tok = new StringTokenizer(categStr, " ");
196         String categ = tok.nextToken();
197         String remoteId = tok.nextToken();
198   InputStream strm = getCacheFile(discId, categ);
199   boolean usingRemote = false;
200   // if it's not there, try the remote database
201   OutputStreamWriter cacheWriter = null;
202   File cacheFile = null;
203   if (strm == null) {
204       setProxy();
205       usingRemote = true;
206       strm = getRemoteData(remoteId, categ);
207       // cache the result if we have a place to put it
208       if (cacheDir != null) {
209     cacheFile = new File(cacheDir, discId + "-" + categ);
210     cacheWriter = new OutputStreamWriter(new FileOutputStream(cacheFile));
211       }
212   }
213   // read the results
214   BufferedReader rdr = new BufferedReader(new InputStreamReader(strm));
215   String str = rdr.readLine();
216   if (cacheWriter != null) cacheWriter.write(str + "\n");
217   if (usingRemote) resetProxy();
218   StringTokenizer rtok = new StringTokenizer(str, " ");
219   int code = Integer.parseInt(rtok.nextToken());
220   if (code != 210) {
221       if (cacheWriter != null) {
222     cacheWriter.close();
223     cacheFile.delete();
224       }
225       return false;
226   }
227   clearTrackNames(ent);
228   while((str = rdr.readLine()) != null) {
229       logOutput += str + "\n";
230       parseResult(ent, str);
231       if (cacheWriter != null)
232     cacheWriter.write(str + "\n");
233   }
234   rdr.close();
235   if (cacheWriter != null)
236       cacheWriter.close();
237   ent.comment = parseComment(ent.comment);
238   for(int i = 0; i < ent.tocs.length; i++) {
239     ent.tocs[i].comment = parseComment(ent.tocs[i].comment);
240   }
241   if (ent.genre.equals("")) ent.genre = capitalize(categ);
242   String sepChar = getSepChar(ent.tocs);
243   fixTracks(ent, sepChar);
244         ent.initialized = true;
245   return true;
246     }
247     /**
248      * Returns a good guess for the character that separates
249      *  the artist from the title in the information received from
250      *  freedb regarding multi-artist CDs.
251      * 
252      * @param tracks the array of tracks.
253      * @return the separator character.
254      */
255     public static String getSepChar(TocEntry[] tracks) {
256         if (isSepChar(tracks, "/")) return "/";
257         if (isSepChar(tracks, "-")) return "-";
258         if (isSepChar(tracks, ":")) return ":";
259         return "/";
260     }
261     /**
262      * Returns the latest query and the result returned.
263      * 
264      * @return the output of the network request.
265      */
266     public String getLogOutput() { return logOutput; }
267     private void setProxy() {
268   if ((proxyHost != null) && !proxyHost.equals("")) {
269       usingProxy = true;
270             systemProperties = System.getProperties();
271       savedProxySet = systemProperties.getProperty("proxySet");
272       savedProxyPort = systemProperties.getProperty("proxyPort");
273       savedProxyHost = systemProperties.getProperty("proxyHost");
274       systemProperties.setProperty("proxySet", "true");
275       systemProperties.setProperty("proxyHost", proxyHost);
276       systemProperties.setProperty("proxyPort", Integer.toString(proxyPort));
277   } else
278       usingProxy = false;
279     }
280     private void resetProxy() {
281   if (usingProxy) {
282       systemProperties.setProperty("proxySet", savedProxySet);
283       systemProperties.setProperty("proxyHost", savedProxyHost);
284       systemProperties.setProperty("proxyPort", savedProxyPort);
285   }
286     }
287     private String parseComment(String str) {
288         if (str == null) return null;
289   int pos;
290   StringBuffer buf = new StringBuffer(str);
291   while((pos = buf.toString().indexOf("\\n")) != -1) {
292       buf.replace(pos, pos + 2, "\n");
293   }
294   while((pos = buf.toString().indexOf("\\t")) != -1) {
295       buf.replace(pos, pos + 2, "\t");
296   }
297   return buf.toString();
298     }
299     private String capitalize(String str) {
300   int len = str.length();
301   if (len == 0) return str;
302   if (len == 1) return str.toUpperCase();
303   return str.substring(0, 1).toUpperCase() + str.substring(1);
304     }
305     private void parseResult(DiscInfo ent, String str) {
306   if (str.startsWith("DTITLE")) {
307       String entry = str.substring(str.indexOf("=") + 1);
308       int p = entry.indexOf("/");
309       ent.artist = entry.substring(0, p).trim();
310       if (ent.artist.equals("Various Artists") || ent.artist.equals("Soundtrack")) ent.multiArtist = true;
311       ent.album = entry.substring(p + 1).trim();
312   } else if (str.startsWith("DYEAR")) {
313       String yr = str.substring(str.indexOf("=") + 1);
314       try { ent.year = Integer.parseInt(yr.trim()); } catch (Exception e) { }
315   } else if (str.startsWith("DGENRE")) {
316       ent.genre = capitalize(str.substring(str.indexOf("=") + 1));
317   } else if (str.startsWith("TTITLE")) {
318       int eq = str.indexOf("=");
319       String trackTitle = str.substring(eq + 1);
320       String trk = str.substring(6, eq);
321       int track = Integer.parseInt(trk);
322       if (track < ent.tocs.length) {
323     ent.tocs[track].title += trackTitle;
324     if (ent.tocs[track].title.equals("")) ent.tocs[track].title = "Untitled";
325       }
326   } else if (str.startsWith("EXTD")) {
327       String comment = str.substring(str.indexOf("=") + 1);
328       if (comment != null)
329     ent.comment += stuff + comment;
330       stuff = "";
331   } else if (str.startsWith("EXTT")) {
332       int pos = str.indexOf("=");
333       String trk = str.substring(4, pos);
334       int track = Integer.parseInt(trk);
335       String comment = stuff + str.substring(pos + 1);
336       stuff = "";
337       if (track < ent.tocs.length) {
338     ent.tocs[track].comment += comment;
339       }
340   } else if (str.startsWith("DISCID")) {
341   } else if (str.startsWith("PLAYORDER")) {
342   } else {
343       if (!str.startsWith("#")) stuff = (str != null) ? str : "";
344   }
345     }
346     private void clearTrackNames(DiscInfo ent) {
347   TocEntry[] tracks = ent.tocs;
348   for(int i = 0; i < tracks.length; i++) {
349       tracks[i].title = "";
350   }
351     }
352     private InputStream getCacheFile(String discId, String categ) {
353   if (cacheDir == null) return null;
354   File cacheFile = new File(cacheDir, discId + "-" + categ);
355   if (!cacheFile.exists()) return null;
356   try {
357       return new FileInputStream(cacheFile);
358   } catch (FileNotFoundException ex) {
359       return null;
360   }
361     }
362     private InputStream getRemoteData(String discId, String categ) {
363   String req = urlString + "?cmd=cddb+read+" + categ + "+" + discId + idStr;
364   logOutput = "request: \"" + req + "\"" + "\n";
365   try {
366       URL url = new URL(req);
367       return url.openStream();
368   } catch (Exception ex) {
369       return null;
370   }
371     }
372     private void fixTracks(DiscInfo ent, String sepChar) {
373   for(int i = 0; i < ent.tocs.length; i++) {
374       TocEntry track = ent.tocs[i];
375       track.album = ent.album;
376       String str = ent.tocs[i].title;
377       if (str == null) str = "";
378       int ind;
379       if (ent.multiArtist && ((ind = str.lastIndexOf(sepChar)) != -1)) {
380     track.artist = str.substring(0, ind).trim();
381     track.title = str.substring(ind + 1).trim();
382       } else {
383     track.artist = ent.artist;
384     track.title = str;
385       }
386       if (!ent.genre.equals("")) track.genre = ent.genre;
387       if (ent.year != 0) track.year = ent.year;
388   }
389     }
390     private static boolean isSepChar(TocEntry[] tracks, String sep) {
391   for(int i = 0; i < tracks.length; i++) {
392       String str = tracks[i].title;
393       if (str.indexOf("Data") != -1) continue;
394             if (str.indexOf(sep) == -1) return false;
395   }
396         return true;
397     }
398     private String idStr, urlString;
399     // part of an attempt to read some broken entries
400     private String stuff;
401     private File cacheDir;
402     private String logOutput;
403     // proxy stuff
404     private boolean usingProxy;
405     private String proxyHost;
406     private int proxyPort;
407     private String savedProxySet, savedProxyHost, savedProxyPort;
408     private Properties systemProperties;
409 }
410 
411 /*
412    Local Variables:
413    mode:java
414    indent-tabs-mode:nil 
415    c-basic-offset:4 
416    c-indent-level:4 
417    c-continued-statement-offset:4 
418    c-brace-offset:-4 
419    c-brace-imaginary-offset:-4 
420    c-argdecl-indent:0
421    c-label-offset:0
422    End:
423 */