App zur Steuerung des mpv Mediaplayers auf einem Raspberry Pi über HTTP
undisclosed
2023-01-08 929228226e08e352769810f729f0e9644a781bec
Calypso neu gebaut auf Nutzung von mpv, alte Fassung entfernt
29 files deleted
2 files modified
7 files added
2 files renamed
3023 ■■■■ changed files
src/de/uhilger/calypso/App.java 147 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/Betrachter.java 143 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/OMXLogLeser.java 165 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/Server.java 146 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/AbstractHandler.java 115 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/BasePlayer.java 115 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/CmdHandler.java 44 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/DBusHandler.java 48 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/FileHandler.java 74 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/LogHandler.java 55 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/MPVKillHandler.java 21 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/MPVPlayHandler.java 27 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/MPVPlayer.java 123 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/MPVSeekHandler.java 62 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/MPlayHandler.java 27 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/MPlayer.java 88 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/OMXPlayer.java 197 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/PingHandler.java 51 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/PlayHandler.java 129 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/PlayOnHandler.java 87 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/Player.java 83 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/SeekHandler.java 68 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/SocketHandler.java 46 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/StopServerHandler.java 50 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/VLCKillHandler.java 25 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/VLCPlayer.java 104 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/handler/VLCSeekHandler.java 58 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/App.java 19 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/AppProperties.java 15 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/MeldeThread.java 10 ●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/ProzessLauscher.java 3 ●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/Rueckmelder.java 46 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/Server.java 35 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/actor/PlayActor.java 87 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/actor/ShellActor.java 73 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/actor/StopServerActor.java 53 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/http/ApiHandler.java 146 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/http/HttpApi.java 97 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/neu/http/Server.java 69 ●●●●● patch | view | raw | blame | history
src/logging.properties 72 ●●●●● patch | view | raw | blame | history
src/de/uhilger/calypso/App.java
File was deleted
src/de/uhilger/calypso/Betrachter.java
File was deleted
src/de/uhilger/calypso/OMXLogLeser.java
File was deleted
src/de/uhilger/calypso/Server.java
File was deleted
src/de/uhilger/calypso/handler/AbstractHandler.java
File was deleted
src/de/uhilger/calypso/handler/BasePlayer.java
File was deleted
src/de/uhilger/calypso/handler/CmdHandler.java
File was deleted
src/de/uhilger/calypso/handler/DBusHandler.java
File was deleted
src/de/uhilger/calypso/handler/FileHandler.java
File was deleted
src/de/uhilger/calypso/handler/LogHandler.java
File was deleted
src/de/uhilger/calypso/handler/MPVKillHandler.java
File was deleted
src/de/uhilger/calypso/handler/MPVPlayHandler.java
File was deleted
src/de/uhilger/calypso/handler/MPVPlayer.java
File was deleted
src/de/uhilger/calypso/handler/MPVSeekHandler.java
File was deleted
src/de/uhilger/calypso/handler/MPlayHandler.java
File was deleted
src/de/uhilger/calypso/handler/MPlayer.java
File was deleted
src/de/uhilger/calypso/handler/OMXPlayer.java
File was deleted
src/de/uhilger/calypso/handler/PingHandler.java
File was deleted
src/de/uhilger/calypso/handler/PlayHandler.java
File was deleted
src/de/uhilger/calypso/handler/PlayOnHandler.java
File was deleted
src/de/uhilger/calypso/handler/Player.java
File was deleted
src/de/uhilger/calypso/handler/SeekHandler.java
File was deleted
src/de/uhilger/calypso/handler/SocketHandler.java
File was deleted
src/de/uhilger/calypso/handler/StopServerHandler.java
File was deleted
src/de/uhilger/calypso/handler/VLCKillHandler.java
File was deleted
src/de/uhilger/calypso/handler/VLCPlayer.java
File was deleted
src/de/uhilger/calypso/handler/VLCSeekHandler.java
File was deleted
src/de/uhilger/calypso/neu/App.java
@@ -1,5 +1,24 @@
/*
    Calypso - Media Player Remote Control via HTTP for Raspberry Pi
    Copyright (C) 2021-2023  Ulrich Hilger
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) 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 Affero General Public License for more details.
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
package de.uhilger.calypso.neu;
import de.uhilger.calypso.neu.http.Server;
import java.util.HashMap;
/**
src/de/uhilger/calypso/neu/AppProperties.java
@@ -59,20 +59,8 @@
    Logger.getLogger(AppProperties.class.getName()).log(Level.INFO, fName);
    InputStream in = null;
    try {
      // Einstellungen aus Datei lesen
      //AppProperties einst = new AppProperties();
      File einstellungenDatei = new File(fName);
      in = new FileInputStream(einstellungenDatei);
      //BufferedReader br = new BufferedReader(new InputStreamReader(in));
      //String line = br.readLine();
      //while(line != null) {
      //  System.out.println(line);
      //  line = br.readLine();
      //}
      //br.close();
      load(in);
      in.close();
    } catch (FileNotFoundException ex) {
@@ -86,8 +74,5 @@
        Logger.getLogger(AppProperties.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
  }
}
src/de/uhilger/calypso/neu/MeldeThread.java
File was renamed from src/de/uhilger/calypso/MeldeThread.java
@@ -16,7 +16,7 @@
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
package de.uhilger.calypso;
package de.uhilger.calypso.neu;
import java.util.logging.Logger;
import java.util.logging.Level;
@@ -45,11 +45,17 @@
    this.meldeUrlStr = meldeUrlStr;
  }
  
  /**
   * ausfuehren des Threads
   */
  @Override
  public void run() {
    try {
      exitValue = omxplayer.waitFor();
      prozessBeendetMelden();
    } catch(Exception ex) {
      lauscher.clear();
      lauscher = null;
    } catch(InterruptedException ex) {
      logger.log(Level.FINE, ex.getMessage(), ex);
    } finally {
      aufraeumen();
src/de/uhilger/calypso/neu/ProzessLauscher.java
File was renamed from src/de/uhilger/calypso/ProzessLauscher.java
@@ -16,8 +16,7 @@
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
package de.uhilger.calypso;
package de.uhilger.calypso.neu;
public interface ProzessLauscher {
  
src/de/uhilger/calypso/neu/Rueckmelder.java
New file
@@ -0,0 +1,46 @@
package de.uhilger.calypso.neu;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 *
 * @author Ulrich Hilger
 */
public class Rueckmelder implements ProzessLauscher {
  @Override
  public void prozessBeendet(String meldeUrlStr) {
    Logger logger = Logger.getLogger(de.uhilger.calypso.neu.http.ApiHandler.class.getName());
    logger.log(Level.INFO,
            "Abspielen beendet, sende Meldung an {0}.",
            new Object[]{meldeUrlStr});
    try {
      HttpURLConnection conn = (HttpURLConnection) new URL(meldeUrlStr).openConnection();
      conn.setRequestMethod("GET");
      conn.connect();
      int status = conn.getResponseCode();
      logger.log(Level.INFO,
              "Abspielen beendet, Meldung an {0} mit Statuscode {1} gesendet.",
              new Object[]{meldeUrlStr, status});
      /*
            fuer den Fall, dass ein Stopp-Signal den Player nicht erreicht
            oder dort nicht funktioniert, gibt es keine Moeglichkeit festzustellen,
            dass der Player noch spielt. Damit in einem solchen Fall der Zeiger
            auf den Abspielprozess nicht verloren geht, wird  der Zeiger nicht
            auf null gesetzt.
       */
      //App.setPlayerProcess(null);
    } catch (IOException ex) {
      logger.log(Level.INFO, ex.getMessage(), ex);
    }
  }
}
src/de/uhilger/calypso/neu/Server.java
File was deleted
src/de/uhilger/calypso/neu/actor/PlayActor.java
New file
@@ -0,0 +1,87 @@
/*
    Calypso - Media Player Remote Control via HTTP for Raspberry Pi
    Copyright (C) 2021-2023  Ulrich Hilger
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) 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 Affero General Public License for more details.
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
package de.uhilger.calypso.neu.actor;
import de.uhilger.calypso.neu.MeldeThread;
import de.uhilger.calypso.neu.Rueckmelder;
import de.uhilger.calypso.neu.http.Server;
import java.io.IOException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * Der PlayActor loest das Abspielen eines Titels aus.
 *
 * Das koennte auch als Shell-Skript geschehen, aber abhaengig
 * vom verwendeten Abspielprogramm benoetigt man das Prozessobjekt
 * des laufenden Abspielprozesses im Verlauf des Abspielens noch, um
 * weitere Kommandos zu senden.
 *
 * Beim Abspieler mpv werden Kommandos allerdings ueber Unix Domain
 * Sockets gesendet, hierfuer waere also das Objekt des laufenden
 * Abspielprozesses nicht noetig. Man benoetigt es aber auch um
 * festzustellen, ob der Abspielprozess beendet ist.
 *
 * @author Ulrich Hilger
 */
public class PlayActor {
  public Process run(Map parameter) {
    String meldeUrlStr = null;
    Object o = parameter.get("r");
    if (o instanceof String) {
      meldeUrlStr = (String) o;
    }
    StringBuilder kommando = new StringBuilder();
    o = parameter.get("titel");
    if (o instanceof String) {
      String titel = (String) o;
      if (titel.toLowerCase().endsWith(".mp3")) {
        kommando.append("mpv --input-ipc-server=/tmp/mpvsocket --no-terminal --vo=null ");
      } else {
        kommando.append("mpv --input-ipc-server=/tmp/mpvsocket --no-terminal ");
      }
      kommando.append(Server.BLANK);
      kommando.append(titel);
      Logger.getLogger(PlayActor.class.getName()).log(Level.FINE, kommando.toString());
      Process player_process;
      try {
        player_process = Runtime.getRuntime().exec(kommando.toString());
        if (meldeUrlStr != null) {
          MeldeThread mt = new MeldeThread();
          mt.setProcess(player_process);
          mt.lauscherHinzufuegen(new Rueckmelder());
          mt.setMeldeUrl(meldeUrlStr);
          mt.start();
        }
        return player_process;
      } catch (IOException ex) {
        Logger logger = Logger.getLogger(PlayActor.class.getName());
        logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
        ex.printStackTrace();
        return null;
      }
    } else {
      Logger.getLogger(PlayActor.class.getName()).log(Level.INFO, "Titel fehlt");
      return null;
    }
  }
}
src/de/uhilger/calypso/neu/actor/ShellActor.java
New file
@@ -0,0 +1,73 @@
/*
    Calypso - Media Player Remote Control via HTTP for Raspberry Pi
    Copyright (C) 2021-2023  Ulrich Hilger
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) 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 Affero General Public License for more details.
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
package de.uhilger.calypso.neu.actor;
import de.uhilger.calypso.neu.http.Server;
import java.io.File;
import java.io.IOException;
/**
 *
 * Der mpv Abspieler erhaelt Kommandos waehrend des Abspielens
 * ueber Unix Domain Sockets. Da diese erst mit Java 16 untersuetzt
 * werden und Java 16 fuer den Raspberry Pi noch nicht erhaeltlich
 * ist, werden diese Kommandos als Shell-Skript unter Verwendung
 * des Linux-Programms socat gesendet.
 *
 * Der Shell Actor fuehrt Skripte aus. Wenn ein Kommando Parameter
 * erfordert, werden diese aus dem Query-Teil des HTTP-Aufrufes
 * entnommen.
 *
 *
 * @author Ulrich Hilger
 */
public class ShellActor {
  /**
   * Ein Skript auf der Kommandozeile ausfuehren
   *
   * @param skriptDir das Verzeichnis, in dem das Skript liegt
   * @param kommando der Name des Skript
   * @throws IOException
   */
  //public void run(String skriptDir, String kommando) throws IOException {
  //  File skriptFile = new File(skriptDir, kommando);
  //  Process p = Runtime.getRuntime().exec(skriptFile.getAbsolutePath());
  //}
  /**
   *
   * @param skriptDir das Verzeichnis, in dem das Skript liegt
   * @param kommando der Name des Skript
   * @param params die Parameter des Shell-Kommandos, null, wenn keine
   * @throws IOException
   */
  public void run(String skriptDir, String kommando, String... params) throws IOException {
    StringBuilder sb = new StringBuilder();
    File skriptFile = new File(skriptDir, kommando);
    sb.append(skriptFile.getAbsolutePath());
    if(params instanceof String[]) {
      for (String param : params) {
        sb.append(Server.BLANK);
        sb.append(param);
      }
    }
    Process p = Runtime.getRuntime().exec(sb.toString());
  }
}
src/de/uhilger/calypso/neu/actor/StopServerActor.java
New file
@@ -0,0 +1,53 @@
/*
    Calypso - Media Player Remote Control via HTTP for Raspberry Pi
    Copyright (C) 2021-2023  Ulrich Hilger
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) 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 Affero General Public License for more details.
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
package de.uhilger.calypso.neu.actor;
import com.sun.net.httpserver.HttpContext;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;
/**
 * Ein HTTP-Handler zum Stoppen der Anwendung
 *
 * @author Ulrich Hilger
 */
public class StopServerActor {
  public void run(HttpContext thisContext) {
    Logger.getLogger(StopServerActor.class.getName()).info("Server wird gestoppt..");
    thisContext.getServer().stop(1); // diesen Server stoppen
    Timer timer = new Timer();
    timer.schedule(new AppStopper(), 1500); // die App auch beenden
  }
  /**
   * Die Klasse AppStopper erm&ouml;glicht das asnychrone bzw.
   * zeitgesteuerte Stoppen der Anwendung.
   */
  class AppStopper extends TimerTask {
    @Override
    public void run() {
      Logger.getLogger(StopServerActor.class.getName()).info("Calypso beendet.");
      System.exit(0);
    }
  }
}
src/de/uhilger/calypso/neu/http/ApiHandler.java
New file
@@ -0,0 +1,146 @@
/*
    Calypso - Media Player Remote Control via HTTP for Raspberry Pi
    Copyright (C) 2021-2023  Ulrich Hilger
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) 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 Affero General Public License for more details.
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
package de.uhilger.calypso.neu.http;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import de.uhilger.calypso.neu.actor.PlayActor;
import de.uhilger.calypso.neu.actor.ShellActor;
import de.uhilger.calypso.neu.actor.StopServerActor;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * Der ApiHandler ist die proprietaer fuer Calypso vorgesehene
 * Weise, HTTP-Anfragen zu verarbeiten.
 *
 * Der ApiHandler ist ein HttpHandler, der vom Server ueber die
 * gesamte Laufzeit des Programmes hinweg gehalten wird. Deswegen
 * beschraenkt sich der Programmcode des ApiHandlers auf die
 * Verteilung der Kommandos auf Aktoren. Die Aktoren werden je nach
 * Bedarf instanziiert und enthalten zusammengenommen den Code
 * des Anwendungskerns.
 *
 * @author Ulrich Hilger
 */
public class ApiHandler extends HttpApi {
  public static final int PLAYERCMD = 2;
  public static final int SERVERCMD = 3;
  public static final String PLAY = "play";
  public static final String PAUSE = "pause";
  public static final String SEEK = "seek";
  public static final String STOP = "stop";
  public static final String SERVER = "server";
  public static final String PING = "ping";
  public static final String PLAYER = "player-proc";
  /**
   * Die HTTP-Anfrage ausführen
   *
   * WICHTIG: Die HTTP-Parameter aus dem Query-Teil muessen in
   * der Reihenfolge sein, die vom Shell-Skript erwartet wird.
   *
   * @param elems die Elemente des URI-Pfads
   * @param parameter die Parameter des Query-Teils der URI
   * @param exchange das Objekt mit Infos zum HTTP-Aufruf
   * @return die Antwort, die gesendet werden soll
   */
  @Override
  protected String process(String[] elems, Map parameter, HttpExchange exchange) {
    String antwort;
    Object o;
    switch (elems[PLAYERCMD]) {
      case PLAY:
        PlayActor a = new PlayActor();
        o = a.run(parameter);
        if (o instanceof Process) {
          exchange.getHttpContext().getAttributes().put(PLAYER, o);
          antwort = "Abspielprozess gestartet.";
        } else {
          // kein Prozess, es ist etwas schief gelaufen
          antwort = "Abspielen konnte nicht gestartet werden.";
        }
        break;
      case PAUSE:
      case SEEK:
        ShellActor pa = new ShellActor();
        try {
          pa.run(getSkriptDir(exchange), elems[PLAYERCMD], toShellParams(parameter));
          antwort = elems[PLAYERCMD] + " ausgefuehrt";
        } catch (IOException ex) {
          Logger.getLogger(ApiHandler.class.getName()).log(Level.SEVERE, null, ex);
          antwort = "Fehler bei der Ausfuehrung von " + elems[PLAYERCMD];
        }
        break;
      case STOP:
        o = exchange.getHttpContext().getAttributes().get(PLAYER);
        if(o instanceof Process) {
          Process p = (Process) o;
          p.destroy();
          antwort = "Player gestoppt.";
        } else {
          antwort = "Es wurde kein Player-Prozess zum Stoppen gefunden.";
        }
        break;
      case SERVER:
        if(elems[SERVERCMD].equals(STOP)) {
          new StopServerActor().run(exchange.getHttpContext());
          antwort = "Calypso: Der Server wird angehalten und die App beendet.";
        } else {
          antwort = elems[SERVERCMD] + " ist ein unbekanntes Serverkommando";
        }
        break;
      case PING:
        antwort = elems[PLAYERCMD];
        break;
      default:
        antwort = "Kommando " + elems[PLAYERCMD] + " unbekannt";
        break;
    }
    return antwort;
  }
  private String[] toShellParams(Map parameter) {
    Collection<String> c = parameter.values();
    return c.toArray(String[]::new);
  }
  private String getSkriptDir(HttpExchange exchange) {
    HttpContext context = exchange.getHttpContext();
    Object o = context.getAttributes().get(Server.SKRIPT_DIR);
    if (o instanceof String) {
      return (String) o;
    } else {
      // kein Skript-Dir
      return "";
    }
  }
}
src/de/uhilger/calypso/neu/http/HttpApi.java
New file
@@ -0,0 +1,97 @@
package de.uhilger.calypso.neu.http;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * Die Klasse HttpApi verwendet die von der Klasse HttpExchange
 * gelieferten Elemente einer HTTP-Anfrage und leitet sie an
 * die abstrakte Methode process, wo Subklassen das jeweilige
 * Kommando aus der HTTP-Anfrage ausfuehren können.
 *
 * @author Ulrich Hilger
 */
public abstract class HttpApi implements HttpHandler {
  public static final String AMP = "&";
  public static final String SLASH = "/";
  @Override
  public void handle(HttpExchange exchange) throws IOException {
    String path = exchange.getRequestURI().getPath();
    String[] elems = path.split(SLASH);
    Map params = getQueryMap(exchange);
    String antwort = process(elems, params, exchange);
    antwortSenden(exchange, params, path, antwort);
  }
  /**
   * Eine HTTP-Anfrage ausführen
   *
   * @param elems die Elemente des URI-Pfads
   * @param parameter die Parameter des Query-Teils der URI
   * @return die Antwort, die gesendet werden soll
   */
  protected abstract String process(String[] elems, Map parameter, HttpExchange exchange);
  /*
    Den Query-Teil einer URL in die Parameter zerlegen
    Die Zerlegung erfolgt mit String.split nach
    &amp; und dann nach =
  */
  protected Map getQueryMap(HttpExchange t) {
    HashMap map = new HashMap();
    String query = t.getRequestURI().getQuery();
    if(query != null && query.length() > 0) {
      String qParts[] = query.split("&");
      for(String qPart : qParts) {
        Logger logger = Logger.getLogger(de.uhilger.calypso.neu.http.HttpApi.class.getName());
        logger.log(Level.FINER, "qPart: {0}", qPart);
        String pParts[] = qPart.split("=");
        map.put(pParts[0], pParts[1]);
        logger.log(Level.FINER, "pParts[0]: {0} pParts[1]: {1}", new Object[]{pParts[0], pParts[1]});
      }
    }
    return map;
  }
  protected void antwortSenden(HttpExchange exchange, Map params, String cmd, String antwort) throws IOException {
    String httpResponseStr = getResponseString(params, cmd, antwort);
    sendResponse(exchange, httpResponseStr);
  }
  protected String getResponseString(Map map, String cmd, String antwort) {
    Set keys = map.keySet();
    StringBuilder buf = new StringBuilder();
    buf.append(cmd);
    buf.append(System.lineSeparator());
    keys.forEach((Object key) -> {
      buf.append("key: ");
      buf.append(key);
      buf.append(System.lineSeparator());
      buf.append("value: ");
      buf.append(map.get(key));
      buf.append(System.lineSeparator());
      //logger.log(Level.FINE, "key {0} value {1}", new Object[]{key, map.get(key)});
    });
    buf.append(antwort);
    return buf.toString();
  }
  protected void sendResponse(HttpExchange t, String response) throws IOException {
    t.sendResponseHeaders(200, response.length());
    OutputStream os = t.getResponseBody();
    os.write(response.getBytes());
    os.close();
  }
}
src/de/uhilger/calypso/neu/http/Server.java
New file
@@ -0,0 +1,69 @@
/*
    Calypso - Media Player Remote Control via HTTP for Raspberry Pi
    Copyright (C) 2021-2023  Ulrich Hilger
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) 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 Affero General Public License for more details.
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
package de.uhilger.calypso.neu.http;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpServer;
import de.uhilger.calypso.neu.AppProperties;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 *
 * @author Ulrich Hilger
 */
public class Server {
  //private static final Logger logger = Logger.getLogger(de.uhilger.calypso.Server.class.getName());
  public static final String CONF = "conf";
  public static final String PORT = "port";
  public static final String SKRIPTE = "skripte";
  public static final String CTX = "ctx";
  public static final String EQUAL = "=";
  public static final String BLANK = " ";
  public static final String SKRIPT_DIR = "skript-dir";
  public void start(AppProperties einst) {
    Logger logger = Logger.getLogger(de.uhilger.calypso.neu.http.Server.class.getName());
    logger.log(Level.INFO, "Server startet auf Port {0}", einst.getString(PORT));
    String skripte = einst.getString(SKRIPTE);
    File skript = new File(skripte, "pause");
    if(skript.exists()) {
      logger.log(Level.INFO, "Skripte gefunden in {0}", skript.getParentFile().getAbsolutePath());
    } else {
      logger.log(Level.INFO, "Skripte nicht gefunden, Ordner laut Einstellungen: {0}", skripte);
    }
    try {
      HttpServer server = HttpServer.create(new InetSocketAddress(einst.getInt(PORT)), 0);
      String ctx = einst.getString(CTX);
      HttpContext context = server.createContext(ctx, new ApiHandler());
      context.getAttributes().put(SKRIPT_DIR, einst.getString(SKRIPTE));
      server.setExecutor(Executors.newFixedThreadPool(20));
      server.start();
    } catch (IOException ex) {
      logger.log(Level.SEVERE, null, ex);
    }
  }
}
src/logging.properties
File was deleted