Persoenliche Mediazentrale
ulrich
2021-04-26 c6fdc483f9e98ae0e461fd58a012584f21c40582
commit | author | age
b56bb3 1 /*
94b1c2 2   Tango - Personal Media Center
b56bb3 3   Copyright (C) 2021  Ulrich Hilger
U 4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU Affero General Public License as
7   published by the Free Software Foundation, either version 3 of the
8   License, or (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU Affero General Public License for more details.
14
15   You should have received a copy of the GNU Affero General Public License
16   along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
94b1c2 18 package de.uhilger.tango.api;
b56bb3 19
1c5fa4 20 import com.google.gson.Gson;
b56bb3 21 import com.sun.net.httpserver.HttpExchange;
94b1c2 22 import de.uhilger.tango.App;
U 23 import de.uhilger.tango.Server;
24 import de.uhilger.tango.entity.Abspielvorgang;
25 import de.uhilger.tango.entity.Abspieler;
26 import de.uhilger.tango.entity.Abspielliste;
27 import de.uhilger.tango.entity.Einstellung;
28 import de.uhilger.tango.entity.Entity;
29 import de.uhilger.tango.entity.Livestream;
30 import de.uhilger.tango.entity.Titel;
31 import de.uhilger.tango.store.FileStorage;
32 import de.uhilger.tango.store.Storage;
005d7a 33 import java.io.IOException;
U 34 import java.net.HttpURLConnection;
35 import java.net.URL;
b56bb3 36 import java.util.HashMap;
U 37 import java.util.Map;
0e9cd3 38 import java.util.logging.Level;
b56bb3 39 import java.util.logging.Logger;
U 40
41 /**
0e9cd3 42  * Die MediaSteuerung verarbeitet HTTP-Signale zur Steuerung von Media-Operationen wie z.B. dem
U 43  * Spielen einer Abspielliste oder dem Starten oder Stoppen eines Videos auf einem entfernten
44  * Abspielgeraet.
45  *
46  * HTTP GET /mz/api/strg/abspieler/play/liste/[name] 
47  * HTTP GET /mz/api/strg/abspieler/ende
48  *
d027b5 49  * HTTP POST /mz/api/strg/abspieler/play/titel mit dem Titel im Body
U 50  * HTTP POST /mz/api/strg/abspieler/play/stream mit dem Livestream im Body (nur Name gefuellt)
c6fdc4 51  * 
U 52  * HTTP POST /mz/api/strg/abspieler/weiter/titel mit dem Titel im Body
0e9cd3 53  *
589850 54  * HTTP GET /mz/api/strg/abspieler/pause 
U 55  * HTTP GET /mz/api/strg/abspieler/stop 
c6fdc4 56  * FALSCH: HTTP GET /mz/api/strg/abspieler/weiter
b56bb3 57  * 
0e9cd3 58  * Faustregel: Anzahl Elemente eines URL plus 1 ist die Anzahl der Elemente des 
U 59  * Ergebnisses von String.split.
60  *
61  *
b56bb3 62  * @author Ulrich Hilger
U 63  * @version 1, 9.4.2021
64  */
65 public class MediaSteuerung extends AbstractHandler {
66
67   private static final Logger logger = Logger.getLogger(MediaSteuerung.class.getName());
0e9cd3 68
f9f819 69   public static final String PL_CMD_PLAY = "play";
0e9cd3 70   public static final String PL_DEFAULT_PARAMS = "?titel=";
005d7a 71   public static final String PL_PARAM_RUECK = "&r=";
U 72   public static final String PL_API_STRG = "/api/strg/"; 
0e9cd3 73   public static final String PL_CMD_ENDE = "ende";
589850 74   public static final String PL_CMD_STOP = "stop";
U 75   public static final String PL_CMD_PAUSE = "pause";
c6fdc4 76   public static final String PL_CMD_PLAYON = "weiter";
f9f819 77   public static final String PL_CMD_CALYPSO_STOP = "stop";
U 78   public static final String PL_CMD_CALYPSO_PAUSE = "pause";
79   public static final String PL_CMD_CALYPSO_PLAYON = "playon";
005d7a 80   public static final String DEFAULT_HOST = "http://localhost:9090";
0e9cd3 81
U 82   private final Map spielt = new HashMap();
b56bb3 83
U 84   @Override
85   protected String get(HttpExchange e) {
0e9cd3 86     String response;
b56bb3 87     String path = e.getRequestURI().toString();
0e9cd3 88     String[] elems = path.split(Server.SLASH);
U 89     FileStorage fs = new FileStorage(App.getInitParameter(App.getRs(App.RB_AP_CONF)));
589850 90     logger.fine(path);
0e9cd3 91     
U 92     // Faustregel: Anzahl Elemente eines URL plus 1 ist die Anzahl der Elemente des 
93     // Ergebnisses von String.split.
94     switch (elems.length) {
95       case 6:
96         if (elems[5].equalsIgnoreCase(PL_CMD_ENDE)) {
97           response = naechsterTitel(fs, elems[4]);
589850 98         } else if(elems[5].equalsIgnoreCase(PL_CMD_STOP)) {
d12f6e 99           spielt.remove(elems[4]);
4bbb9f 100           response = kommandoSenden(fs, elems[4], PL_CMD_CALYPSO_STOP);
589850 101         } else if(elems[5].equalsIgnoreCase(PL_CMD_PAUSE)) {
4bbb9f 102           response = kommandoSenden(fs, elems[4], PL_CMD_CALYPSO_PAUSE);
c6fdc4 103         //} else if(elems[5].equalsIgnoreCase(PL_CMD_PLAYON)) {
U 104         //  response = kommandoSenden(fs, elems[4], PL_CMD_CALYPSO_PLAYON);
0e9cd3 105         } else {
U 106           response = meldung("Ungueltiges Kommando: " + elems[5], AbstractHandler.RTC_NOT_FOUND);
107         }
108         break;
b56bb3 109       case 8:
1c5fa4 110         response = ersterTitel(fs, elems[4], elems[7]);
0e9cd3 111         break;
U 112       default:
113         response = "Ungueltiger URL";
b56bb3 114         break;
U 115     }
116     return response;
589850 117   }
1c5fa4 118
U 119   @Override
120   protected String post(HttpExchange e) {
121     String response;
c6fdc4 122     String abspielerKmd = PL_CMD_PLAY;
U 123     String path = e.getRequestURI().toString();
124     String[] elems = path.split(Server.SLASH);
125     logger.info(elems[5]);
126     if(elems[5].equalsIgnoreCase(PL_CMD_PLAYON)) {
127       abspielerKmd = PL_CMD_CALYPSO_PLAYON;
128     } else if(elems[5].equalsIgnoreCase(PL_CMD_PLAY)){
129       abspielerKmd = PL_CMD_PLAY;
130     }
1c5fa4 131     try {
c6fdc4 132       return urlAbspielen(e, abspielerKmd);
1c5fa4 133     } catch (IOException ex) {
U 134       logger.log(Level.SEVERE, null, ex);
135       return meldung(ex.getLocalizedMessage(), 404);
136     }
137   }
589850 138   
1c5fa4 139   // titel.katalogUrl + titel.pfad + titel.name
c6fdc4 140   private String urlAbspielen(HttpExchange e, String abspielerKmd) throws IOException {
1c5fa4 141     String path = e.getRequestURI().toString();
U 142     String[] elems = path.split(Server.SLASH);
143     FileStorage fs = new FileStorage(App.getInitParameter(App.getRs(App.RB_AP_CONF)));
c6fdc4 144     if(elems[6].equalsIgnoreCase("titel")) {
d027b5 145       String titelJson = bodyLesen(e);
U 146       Gson gson = new Gson();
147       Object o = gson.fromJson(titelJson, fs.typeFromName(Titel.class.getSimpleName()).getType());
148       if(o instanceof Titel) {
149         Titel titel = (Titel) o;
150         String titelUrl = titel.getKatalogUrl() + titel.getPfad() + titel.getName();
151         Entity entity = fs.read(FileStorage.ST_ABSPIELER, elems[4]);
152         if (entity instanceof Abspieler) {
153           Abspieler abspieler = (Abspieler) entity;
154           String server = getEinstellung(fs, App.getRs(App.RB_HOST), DEFAULT_HOST);
c6fdc4 155           String signal = abspielKommando(fs, abspieler, server, titelUrl, abspielerKmd).toString();
d027b5 156           abspielerKommandoSenden(signal);
U 157           return signal + "gesendet.";
158         } else {
159           return meldung("Ungueltiger Abspieler.", 404);
160         }
1c5fa4 161       } else {
d027b5 162         return meldung("Ungueltiger Titel.", 404);
1c5fa4 163       }
c6fdc4 164     } else if(elems[6].equalsIgnoreCase("stream")) {
d027b5 165       String streamJson = bodyLesen(e);
U 166       Gson gson = new Gson();
167       Object o = gson.fromJson(streamJson, fs.typeFromName(Livestream.class.getSimpleName()).getType());
168       if(o instanceof Livestream) {
169         Entity entity = fs.read(FileStorage.ST_LIVESTREAM, ((Livestream) o).getName());
170         if(entity instanceof Livestream) {
171           Livestream stream = (Livestream) entity;
172           entity = fs.read(FileStorage.ST_ABSPIELER, elems[4]);
173           if (entity instanceof Abspieler) {
174             Abspieler abspieler = (Abspieler) entity;
175             String server = "";
c6fdc4 176             String signal = abspielKommando(fs, abspieler, server, stream.getUrl(), PL_CMD_PLAY).toString();
d027b5 177             abspielerKommandoSenden(signal);
U 178             return signal + "gesendet.";
179           } else {
180             return meldung("Ungueltiger Abspieler.", 404);
181           }
182         } else {
183           return meldung("Ungueltiger Livestream.", 404); 
184         }
185       } else {
186        return meldung("Ungueltiger Livestream.", 404); 
187       }      
1c5fa4 188     } else {
d027b5 189       return meldung("Ungueltiger URL.", 404);
658c14 190     }
U 191   }
192   
c6fdc4 193   
589850 194   private String kommandoSenden(Storage s, String aName, String kommando) {
U 195     Entity entity = s.read(FileStorage.ST_ABSPIELER, aName);
196     if (entity instanceof Abspieler) {
197       Abspieler abspieler = (Abspieler) entity;
198       StringBuilder kmd = new StringBuilder();
199       kmd.append(abspieler.getUrl());
200       kmd.append(kommando);
9e14ef 201       String signal = kmd.toString();
c6fdc4 202       //String server = getEinstellung(s, App.getRs(App.RB_HOST), DEFAULT_HOST);
U 203       //String signal = abspielKommando(s, abspieler, server, stream.getUrl(), PL_CMD_PLAY).toString();
9e14ef 204       abspielerKommandoSenden(signal);
U 205       return signal + " gesendet.";
589850 206     } else {
U 207       return meldung("Abspielliste nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
208     }
b56bb3 209   }
c6fdc4 210   
b56bb3 211   
5f7e0b 212   private String ersterTitel(Storage s, String aName, String lName) {
U 213     String response;
214     Entity entity = s.read(FileStorage.ST_ABSPIELLISTE, lName);
215     if (entity instanceof Abspielliste) {
216       Abspielliste liste = (Abspielliste) entity;
217       response = listentitelSpielen(s, aName, liste, 0);
218     } else {
219       response = meldung("Abspielliste nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
220     }
221     return response;
0e9cd3 222   }
U 223
224   private String naechsterTitel(Storage s, String abspielerName) {
225     String response;
226     Object o = spielt.get(abspielerName);
227     if (o instanceof Abspielvorgang) {
228       Abspielvorgang av = (Abspielvorgang) o;
229       Entity entity = s.read(FileStorage.ST_ABSPIELLISTE, av.getListe());
230       if (entity instanceof Abspielliste) {
b56bb3 231         Abspielliste liste = (Abspielliste) entity;
0e9cd3 232         int titelNr = av.getTitelNr();
U 233         if (liste.getTitel().size() > ++titelNr) {
005d7a 234           response = listentitelSpielen(s, abspielerName, liste, titelNr);
0e9cd3 235         } else {
U 236           response = "Liste " + liste.getName() + " ist zuende gespielt.";
589850 237           logger.info(response);
0e9cd3 238         }
U 239       } else {
240         response = meldung("Abspielliste nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
b56bb3 241       }
0e9cd3 242       //response = listenTitelSpielen(e, elems[4]);
U 243     } else {
244       response = meldung("Abspielvorgang fuer Abspieler " + abspielerName, AbstractHandler.RTC_NOT_FOUND);
b56bb3 245     }
U 246     return response;
247   }
0e9cd3 248
005d7a 249   private String listentitelSpielen(Storage s, String aName, Abspielliste liste, int titelNr) {
0e9cd3 250     String response;
U 251     Entity entity = s.read(FileStorage.ST_ABSPIELER, aName);
252     if (entity instanceof Abspieler) {
253       Abspieler abspieler = (Abspieler) entity;
254       String kommando = kommandoFuerTitel(s, liste, abspieler, titelNr);
255       //String kommando = kmd.toString();
256       logger.info(kommando);
5f7e0b 257       abspielerKommandoSenden(kommando);
0e9cd3 258       response = "Abspielen der Liste " + liste.getName() + " auf Abspieler " + aName + " gestartet.";
U 259     } else {
260       response = meldung("Abspieler nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
261     }
262     return response;
263   }
264
265   /**
266    * Das Kommando zum Abspielen fuer den Titel einer Abspielliste und einen bestimmten Abspieler
267    * ermitteln.
268    *
269    * @param s die Ablage, in der Abspieler und Abspiellisten zu finden sind
270    * @param liste Name der Liste, die den gewuenschten Titel enthaelt
271    * @param abspieler Name des Abspielers, der zum Abspielen dienen soll
272    * @param titelNr Nummer des Titels in der Liste
273    * @return das Kommando zum Abspielen (ein URL)
274    */
275   private String kommandoFuerTitel(Storage s, Abspielliste liste, Abspieler abspieler, int titelNr) {
276     // ersten Titel aus Liste holen
277     Titel titel = liste.getTitel().get(titelNr);
278
279     // URL des Titels ermitteln
280     String titelUrl = titel.getKatalogUrl() + titel.getPfad() + titel.getName();
281     logger.log(Level.INFO, "abspielen von {0} auf {1}", new Object[]{titelUrl, abspieler.getUrl()});
282
283     // Titel als 'spielt' vermerken
284     Abspielvorgang vorgang = new Abspielvorgang();
285     vorgang.setAbspieler(abspieler.getName());
286     vorgang.setListe(liste.getName());
287     vorgang.setTitelNr(titelNr);
288     spielt.put(abspieler.getName(), vorgang);
005d7a 289     
U 290     String server = getEinstellung(s, App.getRs(App.RB_HOST), DEFAULT_HOST);
0e9cd3 291
1c5fa4 292     /*
U 293
0e9cd3 294     // Kommando an den Abspieler zusammenbauen
U 295     StringBuilder kmd = new StringBuilder();
296     kmd.append(abspieler.getUrl());
297     kmd.append(PL_CMD_PLAY);
298     // Parameter fuer den Abspieler holen
005d7a 299     kmd.append(getEinstellung(s, App.getRs(App.RB_PLAYERPARAMS), PL_DEFAULT_PARAMS));
U 300     kmd.append(server);
0e9cd3 301     kmd.append(titelUrl);
1c5fa4 302     */
c6fdc4 303     StringBuilder kmd = abspielKommando(s, abspieler, server, titelUrl, PL_CMD_PLAY);
005d7a 304     kmd.append(PL_PARAM_RUECK);
U 305     kmd.append(server);
306     kmd.append(PL_API_STRG);
307     kmd.append(abspieler.getName());
308     kmd.append("/ende");
0e9cd3 309
U 310     return kmd.toString();
311   }
1c5fa4 312
c6fdc4 313   private StringBuilder abspielKommando(Storage s, Abspieler abspieler, String server, String titelUrl, String abspielKmd) {
1c5fa4 314     
U 315     // Kommando an den Abspieler zusammenbauen
316     StringBuilder kmd = new StringBuilder();
317     kmd.append(abspieler.getUrl());
c6fdc4 318     //kmd.append(PL_CMD_PLAY);
U 319     kmd.append(abspielKmd);
1c5fa4 320     // Parameter fuer den Abspieler holen
U 321     kmd.append(getEinstellung(s, App.getRs(App.RB_PLAYERPARAMS), PL_DEFAULT_PARAMS));
322     kmd.append(server);
323     kmd.append(titelUrl);
324
325     return kmd;    
326   }
005d7a 327   
U 328   private String getEinstellung(Storage s, String key, String standardWert) {
329     Entity entity = s.read(Einstellung.class.getSimpleName(), key);
330     if (entity instanceof Einstellung) {
331       Einstellung einstellung = (Einstellung) entity;
332       Object o = einstellung.getValue();
333       if(o instanceof String) {
334         return o.toString();
335       } else {
336         return standardWert;
337       }
338     } else {
339       return standardWert;
340     }
341   }
342   
343   private void abspielerKommandoSenden(String kommando) {
344     /*
345       TODO hier evtl. mit mehreren Versuchen ausgleichen, 
346       dass ein einzelner Versuch nicht 'durchkommt'...
347     */
348     logger.info(kommando);
349     try {
350       HttpURLConnection conn = (HttpURLConnection) new URL(kommando).openConnection();
351       conn.setRequestMethod("GET");
352       conn.connect();
353       int status = conn.getResponseCode();
354       String msg = conn.getResponseMessage();
355       logger.log(Level.INFO, "Kommando {0} mit Status {1} {2} gesendet.", new Object[]{kommando, status, msg});
356     } catch(IOException ex) {
357       logger.log(Level.INFO, ex.getMessage(), ex);
358     }
359   }
360   
5f7e0b 361   private String meldung(String text, int code) {
U 362     setReturnCode(code);
363     return text;
364   }
b56bb3 365 }