/* * Copyright 2004-2010 Information & Software Engineering Group (188/1) * Institute of Software Technology and Interactive Systems * Vienna University of Technology, Austria * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.tuwien.ifs.somtoolbox.apps.viewer.controls.player; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.Socket; import java.net.URL; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import com.martiansoftware.jsap.FlaggedOption; import com.martiansoftware.jsap.JSAP; import com.martiansoftware.jsap.JSAPException; import com.martiansoftware.jsap.JSAPResult; import com.martiansoftware.jsap.UnflaggedOption; import com.martiansoftware.jsap.stringparsers.URLStringParser; import at.tuwien.ifs.somtoolbox.data.metadata.AudioVectorMetaData; /** * @author Jakob Frank * @version $Id: StandalonePlaySOMPlayer.java 3873 2010-10-28 09:29:58Z frank $ */ public class StandalonePlaySOMPlayer extends JFrame implements PlayerListener { private static final long serialVersionUID = 1L; private PlayList playList; private File tmpSongDir; private PocketSOMConnectionHandler connector; public StandalonePlaySOMPlayer(URL url, int pin) { File tmpDir = new File(System.getProperty("java.io.tmpdir") + "/Player-" + System.currentTimeMillis() % 100000); tmpDir.deleteOnExit(); tmpSongDir = new File(tmpDir, "audio"); tmpSongDir.mkdirs(); tmpSongDir.deleteOnExit(); initialize(); try { connector = new PocketSOMConnectionHandler(url, pin); // connector = new PocketSOMConnectionHandler("localhost", 1234); connector.start(); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void initialize() { setDefaultCloseOperation(DISPOSE_ON_CLOSE); this.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { connector.shutdown(); System.exit(0); } }); JPanel main = new JPanel(); main.setLayout(new GridBagLayout()); GridBagConstraints gc = new GridBagConstraints(); gc.gridx = 0; gc.gridy = 0; gc.gridwidth = GridBagConstraints.REMAINDER; gc.weightx = 2; gc.fill = GridBagConstraints.BOTH; this.setContentPane(main); playList = new PlayList(); playList.addPlayerListener(this); JList liste = playList.createMatchingJList(true); JScrollPane scpPlayList = new JScrollPane(liste); main.add(new PlayerControl(playList), gc); gc.gridy += 1; gc.weighty = 1; main.add(scpPlayList, gc); gc.weighty = 0; gc.gridy += 1; JButton btnExtPlayer = new JButton("External"); btnExtPlayer.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { File m3u = File.createTempFile("external", ".m3u"); Iterator<File> iF = playList.getSongs().iterator(); PrintStream ps = new PrintStream(m3u); ps.println("#EXTM3U"); while (iF.hasNext()) { ps.println(); ps.println(iF.next().getAbsolutePath()); } ps.close(); Runtime.getRuntime().exec(String.format("%s %s", "vlc", m3u.getAbsolutePath())); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }); main.add(btnExtPlayer, gc); gc.gridy += 1; /* Core-Functionality done, now some extras */ // Export Playlist // gc.gridx += 1; main.add(createExportPlaylistButton(liste), gc); pack(); } /** * @param args Commandline args */ public static void main(String[] args) { try { JSAP jsap = new JSAP(); jsap.registerParameter(new UnflaggedOption("url", URLStringParser.getParser(), true, "URL of the psom-file")); jsap.registerParameter(new FlaggedOption("pin", JSAP.INTEGER_PARSER, "1234", false, 'p', "pin", "The PIN to access the remote PlaySOM")); JSAPResult res = jsap.parse(args); if (res.success()) { new StandalonePlaySOMPlayer(res.getURL("url"), res.getInt("pin")).setVisible(true); } else { System.err.printf("USAGE: %s%n", jsap.getUsage()); System.exit(1); } } catch (JSAPException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void playStarted(int mode, AudioVectorMetaData song) { // TODO Auto-generated method stub } @Override public void playStopped(int reason, AudioVectorMetaData song) { // TODO Auto-generated method stub } private JButton createExportPlaylistButton(final JList liste) { JButton b = new JButton("Export Playlist"); b.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser fc = new JFileChooser(); if (fc.showSaveDialog(StandalonePlaySOMPlayer.this) != JFileChooser.APPROVE_OPTION) { // Canceled return; } File f = fc.getSelectedFile(); if (!f.getName().endsWith(".m3u")) { f = new File(f.getParentFile(), f.getName() + ".m3u"); } try { PrintStream ps = new PrintStream(f); // Header ps.println("#EXTM3U"); Iterator<File> iF = playList.getSongs().iterator(); Iterator<String> iD = playList.getDataItems().iterator(); while (iF.hasNext() && iD.hasNext()) { File song = iF.next(); String title = iD.next(); ps.printf("%n#EXTINF:-1,%s%n%s%n", title, song); } ps.close(); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } }); return b; } private class PocketSOMConnectionHandler extends Thread { private static final String KEY_BASE_PATH = "collectionURL"; private static final String KEY_CONNECTOR = "playSOM_Address"; private final String basePath; private final int pin; private Socket ctrlSocket, dataSocket; private boolean playLocal = true; public PocketSOMConnectionHandler(URL url, int pin) throws UnknownHostException, IOException { this.pin = pin; HttpURLConnection con = (HttpURLConnection) url.openConnection(); // Read configuration Properties p = new Properties(); p.load(con.getInputStream()); String bp = p.getProperty(KEY_BASE_PATH); if (!bp.endsWith("/")) { bp += "/"; } basePath = bp; String newAddress = p.getProperty(KEY_CONNECTOR); this.setName(getClass().getSimpleName() + " --> " + newAddress); String[] nas = newAddress.split(":"); ctrlSocket = new Socket(nas[0], Integer.parseInt(nas[1])); dataSocket = new Socket(nas[0], Integer.parseInt(nas[1])); } @Override public void run() { try { PrintStream out = new PrintStream(ctrlSocket.getOutputStream(), true, "utf-8"); BufferedReader in = new BufferedReader(new InputStreamReader(ctrlSocket.getInputStream())); PrintStream dataOut = new PrintStream(dataSocket.getOutputStream(), true, "utf-8"); BufferedReader dataIn = new BufferedReader(new InputStreamReader(dataSocket.getInputStream())); // Register if (!login(in, out)) { return; } out.println("Register"); if (!login(dataIn, dataOut)) { return; } if (dataSocket.getInetAddress().isLoopbackAddress()) { playLocal = false; } // Get the initial Playlist handlePlaylistChange(dataIn, dataOut); // Now listen... String line; while ((line = in.readLine()) != null) { if (!line.startsWith("INFO: ")) { continue; } if (line.startsWith("INFO: currentsong")) { handleSongChange(line); } else if (line.startsWith("INFO: playlist changed")) { handlePlaylistChange(dataIn, dataOut); } else { System.out.printf("%s%n", line); } } out.println("Unregister"); out.println("Quit"); dataOut.println("Quit"); out.close(); dataOut.close(); in.close(); dataIn.close(); ctrlSocket.shutdownOutput(); ctrlSocket.shutdownInput(); dataSocket.shutdownOutput(); dataSocket.shutdownInput(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void shutdown() { try { dataSocket.shutdownInput(); dataSocket.shutdownOutput(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private boolean login(BufferedReader in, PrintStream out) throws IOException { out.println("Login: Pin"); out.println("PIN: " + pin); String response = in.readLine(); if (!response.equals("Login: OK")) { System.err.printf("Login failed! (%s)%n"); return false; } // out.println("Register"); return true; } private void handlePlaylistChange(BufferedReader dataIn, PrintStream dataOut) { try { dataOut.println("Get: PlayList"); playList.clearPlaylist(); HashMap<String, File> dlList = new HashMap<String, File>(); String line; while ((line = dataIn.readLine()) != null) { if (line.startsWith("Song:")) { String song = line.substring(line.indexOf(' ') + 1); File songfile = new File(tmpSongDir, song); songfile.createNewFile(); dlList.put(basePath + song, songfile); playList.addSong(song, songfile); } else if (line.equalsIgnoreCase("Playlist")) { // Do nothing } else if (line.equalsIgnoreCase("EndList")) { // Ensure repainting. StandalonePlaySOMPlayer.this.validate(); break; } else { System.err.printf("Illegal Command: \"%s\"%n", line); break; } } new MusicDownloadThread(dlList).start(); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.printf("x%n"); } private void handleSongChange(String line) { String[] ls = line.split("\\s+", 3); if (ls.length > 2) { String song = ls[2]; playList.stop(); if (playLocal) { playList.play(playList.getIndexOf(song)); } else { playList.setCurrentSong(playList.getIndexOf(song)); } } else { playList.stop(); } System.out.printf("x%n"); } private class MusicDownloadThread extends Thread { private final HashMap<String, File> dlList; private final String basename; private static final String USER_AGENT = "PlaySOMPlayer/1.1 (standalone)"; public MusicDownloadThread(HashMap<String, File> dlList) { basename = getClass().getSimpleName(); setName(basename + " (idle)"); this.dlList = dlList; } private void setProgress(int currentItem) { setName(basename + " (" + currentItem + "/" + dlList.size() + ")"); } @Override public void run() { int i = 0; Set<Entry<String, File>> list = dlList.entrySet(); for (Entry<String, File> entry : list) { try { setProgress(++i); URL url = new URL(entry.getKey()); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestProperty("User-Agent", USER_AGENT); con.connect(); InputStream is = con.getInputStream(); FileOutputStream os = new FileOutputStream(entry.getValue()); byte[] buffer = new byte[1024]; int len; int all = 0; while ((len = is.read(buffer)) > 0) { os.write(buffer, 0, len); all += len; } os.flush(); is.close(); os.close(); con.disconnect(); System.out.printf("Got %s (%.2fkB)%n", entry.getValue().getName(), all / 1024d); } catch (MalformedURLException e1) { // TODO Auto-generated catch block e1.printStackTrace(); entry.getValue().delete(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); entry.getValue().delete(); } } setName(basename + " (got " + dlList.size() + ")"); } } } }