Package net.jlastfm

Source Code of net.jlastfm.LastFM$DataDispatcher

package net.jlastfm;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.jlastfm.listeners.LastFmListener;
import net.jlastfm.model.LastFmPlaylist;
import net.jlastfm.model.LastFmTrack;

import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;
/**
* This class provides access to all implemented LastFM functions,
* a radio stream and information on the currently played playlist.
*
* To connect to LastFM create a new instance of this class using either
* the default constructor and the connect method or the parameterized
* constructor which uses the connect method to establish a connection. 
*
* Next some {@link LastFmListener} may be added to playback the
* stream with an audio player or simply display the meta data.
*
*/
public class LastFM extends Thread {

  // Meta data about this client.
  public static final String VERSION = "0.1a";
 
  // List of registered listeners.
  private List<LastFmListener> listeners;

  /*
   *  Contains session related data like streaming url and session
   *  id. 
   */
  private Map<String, String> data;

  // User credential
  private String username;
  private String passwordMd5;
 
  private boolean connected; 
  private boolean running;
 
  // LastFM connection data
  private Socket lastFmSocket;
  private InputStream lastFmInputStream;
 
  private LastFmPlaylist playlist;
  private URL playlistUrl;
  private PlaylistParser playlistParser;
   
  private Logger logger;
 
  private boolean skipTrack;
 
  /**
   * Creates a new instance which is not connected to lastfm.
   * Connect has to be called explizit.
   */
  public LastFM() {
    this.listeners = new ArrayList<LastFmListener>();
    this.running = true;
    this.logger = java.util.logging.Logger.getLogger("LastFmStream");
    this.playlistParser = new PlaylistParser();
    this.skipTrack = false;
   
    logger.setLevel(Level.FINEST);
  }
 
  /**
   * Creates a new instance and connects to lastfm with the
   * passed on parameters.
   *
   * @param username to logon.
   * @param password as md5 hash for username.
   */
  public LastFM(String username, String passwordMd5) {
    this.username = username;
    this.passwordMd5 = passwordMd5;
  }
 
  public void addListener(LastFmListener listener) {
    synchronized (this.listeners) {
      this.listeners.add(listener);
    }
  }
 
  public void removeListener(LastFmListener listener) {
    synchronized (this.listeners) {
      this.listeners.remove(listener);
    }
  }
 
  /**
   * Connects to lastfm and starts streaming to all registered
   * {@link LastFmListener} using the passed on credential.
   *
   * @param username of the user to login.
   * @param passwordMd5 MD5 sum of the password.
   * @throws LastFmException if the connection could not established.
   */
  public void connect(String username, String passwordMd5) {
    this.username = username;
    this.passwordMd5 = passwordMd5;
   
    StringBuffer handshakeUrl = new StringBuffer();
    handshakeUrl.append("http://ws.audioscrobbler.com/radio/handshake.php?version=");
    handshakeUrl.append(LastFM.VERSION);
    handshakeUrl.append("&platform=linux&username=");
    handshakeUrl.append(this.username);
    handshakeUrl.append("&passwordmd5=");
    handshakeUrl.append(this.passwordMd5);
    handshakeUrl.append("&language=en&player=aTunes");
   
    URL url;
    try {
      url = new URL(handshakeUrl.toString());
      URLConnection connection = url.openConnection();
     
      InputStream inputStream = new BufferedInputStream(connection.getInputStream());
      byte[] buffer = new byte[4069];

      int read = 0;
      StringBuffer result = new StringBuffer();
      while ((read = inputStream.read(buffer)) > -1) {
        result.append((new String(buffer, 0, read)));
      }
     
      String [] rows = result.toString().split("\n");
      this.data = new HashMap<String, String>();
     
      for (String row : rows) {
        row = row.trim();
        int firstEquals = row.indexOf("=");
        data.put(row.substring(0, firstEquals), row.substring(firstEquals + 1));
      }

      String streamingUrl = data.get("stream_url")// URL of the users radio stream
      streamingUrl = streamingUrl.substring(7);     // cut http://
      int delimiter = streamingUrl.indexOf("/");    // seperate URL and path
      String hostname = streamingUrl.substring(0, delimiter);
      String path = streamingUrl.substring(delimiter + 1);
     
      String[] tokens = hostname.split(":");      // seperate hostname and port
      hostname = tokens[0];
      int port = Integer.parseInt(tokens[1]);
         
      this.lastFmSocket = new Socket(hostname, port);
     
      // Request to start the audio stream
      OutputStreamWriter osw = new OutputStreamWriter(this.lastFmSocket.getOutputStream());
      osw.write("GET /" + path + " HTTP/1.0\r\n");
      osw.write("Host: " + hostname + "\r\n");
      osw.write("\r\n")
      osw.flush();
     
      // Answer of request. If return code 200 the following data of this stream
      // are of the mp3 music stream.
      this.lastFmInputStream = this.lastFmSocket.getInputStream();
      result = new StringBuffer();
      while ((read = this.lastFmInputStream.read(buffer)) > -1) {
        String line = new String(buffer, 0, read);
        result.append(line);
        if (line.contains("\r\n\r\n"))
          break;
      }
     
      String response = result.toString();
      logger.info("Result: " + response);
      if (!response.startsWith("HTTP/1.0 200 OK")) {
        this.lastFmSocket.close();
        throw new LastFmException("Could not handshake with lastfm. Check credential!");
      }
           
      StringBuffer sb = new StringBuffer();
      sb.append("http://");
      sb.append(this.data.get("base_url"));
      sb.append(this.data.get("base_path"));
      sb.append("/xspf.php?sk=");
      sb.append(this.data.get("session"));
      sb.append("&discovery=1&desktop=");
      sb.append(LastFM.VERSION);
      logger.info(sb.toString());
      this.playlistUrl = new URL(sb.toString());
      this.playlist = this.playlistParser.fetchPlaylist(this.playlistUrl.toString());

      Iterator<LastFmTrack> it = this.playlist.iterator();
      while (it.hasNext()) {
        System.out.println(it.next().getCreator());
      }
     
      this.connected = true;
     
    } catch (MalformedURLException e) {
      throw new LastFmException("Could not handshake with lastfm", e.getCause());
    } catch (IOException e) {
      throw new LastFmException("Could not initialise lastfm", e.getCause());
    }
   
  }
 
  /**
   * Returns the current playlist used to play
   * songs.
   *
   * @return the current playlist.
   */
  public LastFmPlaylist getPlaylist() {
    logger.severe("getPlaylist");
    if (!this.connected) {
      throw new LastFmException("Not connected.");
   
    this.playlist = this.playlistParser.fetchPlaylist(this.playlistUrl.toString());
    return this.playlist;
  }

  /**
   * Disconnects from lastFM. To reconnect simply call
   * connect. The thread keeps running.
   */
  public void disconnect() {
    this.connected = false;
  }
 
  /**
   * Skipps the current track.
   */
  public void skip() {
    logger.entering(this.getClass().getName(), "skip");
   
    // Build skip URL
    StringBuffer skipUrl = new StringBuffer();
    skipUrl.append("control.php?command=skip&session=");
    skipUrl.append(this.data.get("session"));
   
    // Request to start the audio stream
    OutputStreamWriter osw;
    try {
      osw = new OutputStreamWriter(this.lastFmSocket.getOutputStream());
      osw.write("GET /" + skipUrl.toString() + " HTTP/1.0\r\n");
      osw.write("Host: " + this.lastFmSocket.getInetAddress().getHostName() + "\r\n");
      osw.write("\r\n")
      osw.flush();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }       
    this.skipTrack = true;
    logger.exiting(this.getClass().getName(), "skip");
  }
 
  /**
   * Returns the connection state.
   *
   * @return true if successfully connected to lastFM, otherwise false.
   */
  public boolean isConnected() {
    return connected;
  }

  /**
   *
   * @return
   */
  public boolean isRunning() {
    return running;
  }

  /**
   * Closes the connection to lastFm and removes all
   * registered {@link LastFmListener}.
   */
  public void shutdown() {
    this.running = false;
    synchronized (this.listeners) {
      Iterator<LastFmListener> it = this.listeners.iterator();
      while (it.hasNext()) {
        it.next();
        it.remove();
      }
    }
  }
 

  @Override
  public void run() {
   
    Iterator<LastFmTrack> it = null;
   
    while (this.running) {
      try {
        Thread.sleep(500);
      } catch (InterruptedException e) {
        this.logger.log(Level.FINER, "Thread interrupted");
      }
      // Wait until connected to lastfm.
      while (this.running && !this.connected) {
        continue;
      }
     
      // When left wait loop and not connected end thread.
      if (!this.connected) {
        break;
      }
     
      if (it == null) {
        it = this.playlist.iterator();
      }
     
      // If all Tracks are played fetch a new playlist
      // and continue at the beginning of this loop.
      if (!it.hasNext()) {
        this.playlist = this.getPlaylist();
        it = this.playlist.iterator();
        continue;
      }
     
      LastFmTrack currentTrack = it.next();     
      // Read stream
      BufferedInputStream is = null;
      try {
        URL trackUrl = new URL(currentTrack.getLocation());
        URLConnection connection = trackUrl.openConnection();
        is = new BufferedInputStream(connection.getInputStream());
       
        byte[] buffer = new byte[2048];
        int bytesRead = 0;
       
        synchronized(this.listeners) {
          for (LastFmListener listener : this.listeners) {
            listener.startTrack(currentTrack.getCreator(), currentTrack.getTitle(), currentTrack.getAlbum());
          }
        }
       
        while (!this.skipTrack && this.connected && (bytesRead = is.read(buffer)) > -1) {
          byte[] currentData = new byte[bytesRead];
          System.arraycopy(buffer, 0, currentData, 0, bytesRead);
          synchronized (this.listeners) {
            for (LastFmListener listener : this.listeners) {
              Thread t = new Thread(new DataDispatcher(currentData, listener));
              t.start();
            }
          }
        }
       
      } catch (IOException e) {
        logger.info("Exception while reading radio data.");
        //throw new LastFmException("Error while read from radio stream", e);
      } finally {
        try {
          if (is != null)
            is.close();
        } catch (IOException e) {
          logger.info("Could not close radio socket.");
        }
        this.skipTrack = false;
      }     
    }
    logger.exiting(this.getClass().getName(), "run()");
  }
 
  /*
   * Used to pass the radio stream data to the listeners.
   *
   */
  private class DataDispatcher implements Runnable {
   
    private byte[] data;
    private LastFmListener listener;
   
    public DataDispatcher(byte[] data, LastFmListener listener) {
      this.data = data;
      this.listener = listener;
    }
   
    @Override
    public void run() {
      this.listener.newChunkAvailable(data);     
    }   
  }

}
TOP

Related Classes of net.jlastfm.LastFM$DataDispatcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.