/*
* Jajuk
* Copyright (C) The Jajuk Team
* http://jajuk.info
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package org.jajuk.services.players;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import org.apache.commons.lang.StringUtils;
import org.jajuk.base.File;
import org.jajuk.events.JajukEvent;
import org.jajuk.events.JajukEvents;
import org.jajuk.events.ObservationManager;
import org.jajuk.services.webradio.WebRadio;
import org.jajuk.util.Conf;
import org.jajuk.util.Const;
import org.jajuk.util.UtilString;
import org.jajuk.util.error.JajukException;
import org.jajuk.util.log.Log;
/**
* Jajuk web radio player implementation based on Mplayer.
*/
public class WebRadioPlayerImpl extends AbstractMPlayerImpl {
/**
* Reader : read information from mplayer like position.
*/
private class ReaderThread extends Thread {
/**
* Implemented to set a useful thread name.
*/
public ReaderThread() {
super("WebRadio Reader Thread");
}
/* (non-Javadoc)
* @see java.lang.Thread#run()
*/
@Override
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = null;
for (;;) {
line = in.readLine();
if (line == null) {
break;
}
if (line.startsWith(("ICY Info:"))) {
//Send an event that web radio info has been updated
Properties pDetails = new Properties();
String radioTrackDetail = "";
// Some stations doesn't include the StreamUrl in the ICY line. Sample :
//ICY Info: StreamTitle='-- Now On Air: URB Non-Stop :: Playing: xxx Cilmi - Sweet About Me :: Email the station xxx@yyy.uk --';
if (line.contains("';StreamUrl")) {
radioTrackDetail = line.substring(line.indexOf("StreamTitle='") + 13,
line.indexOf("';StreamUrl"));
// Otherwise, the line should ends with ','
} else if (line.endsWith("';")) {
radioTrackDetail = line.substring(line.indexOf("StreamTitle='") + 13,
line.length() - 2);
} else {
// Just in case, we also handle the case where the line doesn't ends with ';
radioTrackDetail = line.substring(line.indexOf("StreamTitle='") + 13,
line.length() - 2);
}
String currentRadioTrack = QueueModel.getCurrentRadio().getName();
if (StringUtils.isNotEmpty(radioTrackDetail)) {
currentRadioTrack += ":: " + radioTrackDetail;
}
pDetails.put(Const.DETAIL_CONTENT, QueueModel.getCurrentRadio());
pDetails.put(Const.CURRENT_RADIO_TRACK, currentRadioTrack);
ObservationManager.notify(new JajukEvent(JajukEvents.WEBRADIO_INFO_UPDATED, pDetails));
}
bOpening = false;
// Search for Exiting (...) pattern
if (line.matches(".*\\x2e\\x2e\\x2e.*\\(.*\\).*")) {
bEOF = true;
}
}
// can reach this point at the end of file
in.close();
bEOF = true;
return;
} catch (Exception e) {
Log.error(e);
}
}
}
/**
* (non-Javadoc).
*
* @param radio
* @param fVolume
*
* @throws IOException Signals that an I/O exception has occurred.
* @throws JajukException the jajuk exception
*
* @see org.jajuk.players.IPlayerImpl#play(org.jajuk.base.File, float, long,
* float)
*/
@Override
public void play(WebRadio radio, float fVolume) throws IOException, JajukException {
this.fVolume = fVolume;
this.bOpening = true;
this.bEOF = false;
this.bitPerfect = Conf.getBoolean(Const.CONF_BIT_PERFECT);
// Start
ProcessBuilder pb = new ProcessBuilder(buildCommand(radio.getUrl(), 0));
Log.debug("Using this Mplayer command: {{" + pb.command() + "}}");
// Set all environment variables format: var1=xxx var2=yyy
try {
Map<String, String> env = pb.environment();
StringTokenizer st = new StringTokenizer(Conf.getString(Const.CONF_ENV_VARIABLES), " ");
while (st.hasMoreTokens()) {
StringTokenizer st2 = new StringTokenizer(st.nextToken(), "=");
env.put(st2.nextToken(), st2.nextToken());
}
// If needed, set proxy settings in format:
// http_proxy=http://username:password@proxy.example.org:8080
if (Conf.getBoolean(Const.CONF_NETWORK_USE_PROXY)) {
String sLogin = Conf.getString(Const.CONF_NETWORK_PROXY_LOGIN).trim();
String sHost = Conf.getString(Const.CONF_NETWORK_PROXY_HOSTNAME).trim();
int port = Conf.getInt(Const.CONF_NETWORK_PROXY_PORT);
// Non anonymous proxy
if (!StringUtils.isBlank(sLogin)) {
String sPwd = UtilString.rot13(Conf.getString(Const.CONF_NETWORK_PROXY_PWD));
String sProxyConf = "http://" + sLogin + ':' + sPwd + '@' + sHost + ':' + port;
env.put("http_proxy", sProxyConf);
Log.debug("Using these proxy settings: " + sProxyConf);
}
// Anonymous proxy
else {
String sProxyConf = "http://" + sHost + ':' + port;
env.put("http_proxy", sProxyConf);
Log.debug("Using these proxy settings: " + sProxyConf);
}
}
} catch (Exception e) {
Log.error(e);
}
// Start mplayer
proc = pb.start();
// start mplayer replies reader thread
new ReaderThread().start();
// if opening, wait, 30 secs max
int i = 0;
while (bOpening && i < 30) {
try {
Thread.sleep(1000);
i++;
} catch (InterruptedException e) {
Log.error(e);
}
}
// If end of file already reached, it means that file cannot be read
if (bEOF) {
throw new JajukException(7);
}
// Get track length
sendCommand("get_time_length");
}
/*
* (non-Javadoc)
*
* @see org.jajuk.players.AbstractMPlayerImpl#play(org.jajuk.base.File, float,
* long, float)
*/
@Override
public void play(File file, float position, long length, float volume) {
// nothing to do here...
}
/* (non-Javadoc)
* @see org.jajuk.services.players.IPlayerImpl#getActuallyPlayedTimeMillis()
*/
@Override
public long getActuallyPlayedTimeMillis() {
// makes no sense for webradios
return 0;
}
}