package com.sleazyweasel.applescriptifier; import com.google.gson.Gson; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; public class MusicPlayerServlet extends HttpServlet { private final MusicPlayerSupplier musicPlayer; private AtomicReference<MusicPlayerState> musicPlayerState = new AtomicReference<MusicPlayerState>(new MusicPlayerState(false, "", "", "", "", MusicPlayerInputType.NONE, new HashMap<Integer, String>(), "", "", false, "", 1.0)); public MusicPlayerServlet(MusicPlayerSupplier players) { this.musicPlayer = players; } public void init() { musicPlayer.addListener(new MusicPlayer.MusicPlayerStateChangeListener() { public void stateChanged(MusicPlayer player, MusicPlayerState state) { musicPlayerState.set(state); } }); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException { // Set to expire far in the past. response.setHeader("Expires", "Sat, 6 May 1995 12:00:00 GMT"); // Set standard HTTP/1.1 no-cache headers. response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); // Set IE extended HTTP/1.1 no-cache headers (use addHeader). response.addHeader("Cache-Control", "post-check=0, pre-check=0"); // Set standard HTTP/1.0 no-cache header. response.setHeader("Pragma", "no-cache"); response.setContentType("application/json; charset=utf-8"); response.setCharacterEncoding("UTF-8"); String pathInfo = req.getPathInfo(); if (pathInfo.startsWith("/airfoilstatusdata")) { PrintWriter writer = response.getWriter(); MusicPlayerState musicPlayerState = this.musicPlayerState.get(); writer.println(musicPlayerState.getAlbumArtUrl()); writer.println(musicPlayerState.getTitle()); writer.println(musicPlayerState.getArtist()); writer.println(musicPlayerState.getAlbum()); writer.println(musicPlayerState.getDuration()); writer.println(); writer.flush(); return; } else if (pathInfo.startsWith("/playpause")) { musicPlayer.playPause(); sleep(); } else if (pathInfo.startsWith("/thumbsup")) { musicPlayer.thumbsUp(); sleep(); } else if (pathInfo.startsWith("/thumbsdown")) { musicPlayer.thumbsDown(); sleep(); } else if (pathInfo.startsWith("/next")) { musicPlayer.next(); sleep(); } else if (pathInfo.startsWith("/keyStroke")) { char key = req.getParameter("key").charAt(0); if (key == 's') { musicPlayer.askToChooseStation(); sleep(); } } else if (pathInfo.startsWith("/bounce")) { musicPlayer.bounce(); sleep(); } else if (pathInfo.startsWith("/volumeUp")) { musicPlayer.volumeUp(); sleep(); } else if (pathInfo.startsWith("/volumeDown")) { musicPlayer.volumeDown(); sleep(); } else if (pathInfo.startsWith("/textEntry")) { //todo deprecate this command altogether, as it's a giant hack. String text = req.getParameter("text"); //trim the user data to protect against possible buffer overflows in pianobar. if (text.length() > 100) { text = text.substring(0, 100); } if (text != null && text.length() != 0) { //the only time we ever do this is from the client is to enter "" to cancel station selection. //so do nothing for now...? // if (text.length() == 1 && !Character.isDigit(text.charAt(0))) { // musicPlayer.sendKeyStroke(text.charAt(0)); // } else { // sendTextCommand(text); // } } else { musicPlayer.cancelStationSelection(); } sleep(); } else if (pathInfo.startsWith("/selectStation")) { String stationId = req.getParameter("id"); musicPlayer.selectStation(Integer.valueOf(stationId)); } else if (pathInfo.startsWith("/albumArt")) { populateResponseDataFromMusicPlayerState(new HashMap<String, Object>()); Map<String, String> responseData = new HashMap<String, String>(1); responseData.put("albumArtUrl", musicPlayerState.get().getAlbumArtUrl()); response.getWriter().append(new Gson().toJson(responseData)); return; } appendStatus(response); } private void appendStatus(HttpServletResponse response) throws IOException { appendStatus(response, new HashMap<String, Object>()); } private void appendStatus(HttpServletResponse response, Map<String, Object> responseData) throws IOException { populateResponseDataFromMusicPlayerState(responseData); response.getWriter().append(new Gson().toJson(responseData)); } private void populateResponseDataFromMusicPlayerState(Map<String, Object> responseData) { musicPlayer.activate(); MusicPlayerState state = musicPlayerState.get(); boolean inputRequested = state.isInputRequested(); responseData.put("station", state.getStation()); responseData.put("artist", state.getArtist()); responseData.put("album", state.getAlbum()); responseData.put("title", state.getTitle()); responseData.put("heart", state.isCurrentSongIsLoved() ? "YES" : "NO"); responseData.put("playing", state.isPlaying() ? "YES" : "NO"); if (inputRequested && state.getInputTypeRequested().equals(MusicPlayerInputType.CHOOSE_STATION)) { Map<Integer, String> stations = state.getStations(); responseData.put("stations", stations); responseData.put("inputType", "stationSelection"); } responseData.put("inputRequested", inputRequested ? "YES" : "NO"); } private void sleep() { try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException("interrupted"); } } // private void sendTextCommand(String command) { // playerSupplier.sendTextCommand(command); // } }