Persoenliche Mediazentrale
ulrich
2022-05-11 3866a756db6c85e36d1896af8461682c8c19a2ec
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 
b56bb3 56  * 
0e9cd3 57  * Faustregel: Anzahl Elemente eines URL plus 1 ist die Anzahl der Elemente des 
U 58  * Ergebnisses von String.split.
59  *
60  *
b56bb3 61  * @author Ulrich Hilger
U 62  * @version 1, 9.4.2021
63  */
64 public class MediaSteuerung extends AbstractHandler {
65
66   private static final Logger logger = Logger.getLogger(MediaSteuerung.class.getName());
0e9cd3 67
f9f819 68   public static final String PL_CMD_PLAY = "play";
0e9cd3 69   public static final String PL_DEFAULT_PARAMS = "?titel=";
005d7a 70   public static final String PL_PARAM_RUECK = "&r=";
960359 71   public static final String PL_API_STRG = "api/strg/"; 
0e9cd3 72   public static final String PL_CMD_ENDE = "ende";
589850 73   public static final String PL_CMD_STOP = "stop";
392dc9 74   public static final String PL_CMD_VOLDN = "voldn";
U 75   public static final String PL_CMD_VOLUP = "volup";
589850 76   public static final String PL_CMD_PAUSE = "pause";
c6fdc4 77   public static final String PL_CMD_PLAYON = "weiter";
f9f819 78   public static final String PL_CMD_CALYPSO_STOP = "stop";
392dc9 79   public static final String PL_CMD_CALYPSO_VOL_INC = "vol-inc";
U 80   public static final String PL_CMD_CALYPSO_VOL_DEC = "vol-dec";
f9f819 81   public static final String PL_CMD_CALYPSO_PAUSE = "pause";
U 82   public static final String PL_CMD_CALYPSO_PLAYON = "playon";
005d7a 83   public static final String DEFAULT_HOST = "http://localhost:9090";
0e9cd3 84
ad3e2d 85   public static final String RB_HOST = "host";
U 86   public static final String RB_PLAYERPARAMS = "playerparams";
87   
0e9cd3 88   private final Map spielt = new HashMap();
f70acb 89   
U 90   private String conf;
91   
92   public MediaSteuerung(String conf) {
93     this.conf = conf;
94   }
b56bb3 95
U 96   @Override
97   protected String get(HttpExchange e) {
0e9cd3 98     String response;
b56bb3 99     String path = e.getRequestURI().toString();
0e9cd3 100     String[] elems = path.split(Server.SLASH);
f70acb 101     FileStorage fs = new FileStorage(conf);
589850 102     logger.fine(path);
0e9cd3 103     
U 104     // Faustregel: Anzahl Elemente eines URL plus 1 ist die Anzahl der Elemente des 
105     // Ergebnisses von String.split.
106     switch (elems.length) {
107       case 6:
108         if (elems[5].equalsIgnoreCase(PL_CMD_ENDE)) {
109           response = naechsterTitel(fs, elems[4]);
589850 110         } else if(elems[5].equalsIgnoreCase(PL_CMD_STOP)) {
d12f6e 111           spielt.remove(elems[4]);
4bbb9f 112           response = kommandoSenden(fs, elems[4], PL_CMD_CALYPSO_STOP);
392dc9 113         } else if(elems[5].equalsIgnoreCase(PL_CMD_VOLDN)) {
U 114           response = kommandoSenden(fs, elems[4], PL_CMD_CALYPSO_VOL_DEC);
115         } else if(elems[5].equalsIgnoreCase(PL_CMD_VOLUP)) {
116           response = kommandoSenden(fs, elems[4], PL_CMD_CALYPSO_VOL_INC);
589850 117         } else if(elems[5].equalsIgnoreCase(PL_CMD_PAUSE)) {
4bbb9f 118           response = kommandoSenden(fs, elems[4], PL_CMD_CALYPSO_PAUSE);
c6fdc4 119         //} else if(elems[5].equalsIgnoreCase(PL_CMD_PLAYON)) {
U 120         //  response = kommandoSenden(fs, elems[4], PL_CMD_CALYPSO_PLAYON);
0e9cd3 121         } else {
U 122           response = meldung("Ungueltiges Kommando: " + elems[5], AbstractHandler.RTC_NOT_FOUND);
123         }
124         break;
b56bb3 125       case 8:
1c5fa4 126         response = ersterTitel(fs, elems[4], elems[7]);
0e9cd3 127         break;
U 128       default:
129         response = "Ungueltiger URL";
b56bb3 130         break;
U 131     }
132     return response;
589850 133   }
1c5fa4 134
U 135   @Override
136   protected String post(HttpExchange e) {
137     String response;
c6fdc4 138     String abspielerKmd = PL_CMD_PLAY;
U 139     String path = e.getRequestURI().toString();
140     String[] elems = path.split(Server.SLASH);
141     logger.info(elems[5]);
142     if(elems[5].equalsIgnoreCase(PL_CMD_PLAYON)) {
143       abspielerKmd = PL_CMD_CALYPSO_PLAYON;
144     } else if(elems[5].equalsIgnoreCase(PL_CMD_PLAY)){
145       abspielerKmd = PL_CMD_PLAY;
146     }
1c5fa4 147     try {
c6fdc4 148       return urlAbspielen(e, abspielerKmd);
1c5fa4 149     } catch (IOException ex) {
U 150       logger.log(Level.SEVERE, null, ex);
151       return meldung(ex.getLocalizedMessage(), 404);
152     }
153   }
589850 154   
1c5fa4 155   // titel.katalogUrl + titel.pfad + titel.name
c6fdc4 156   private String urlAbspielen(HttpExchange e, String abspielerKmd) throws IOException {
1c5fa4 157     String path = e.getRequestURI().toString();
U 158     String[] elems = path.split(Server.SLASH);
f70acb 159     FileStorage fs = new FileStorage(conf);
c6fdc4 160     if(elems[6].equalsIgnoreCase("titel")) {
d027b5 161       String titelJson = bodyLesen(e);
U 162       Gson gson = new Gson();
163       Object o = gson.fromJson(titelJson, fs.typeFromName(Titel.class.getSimpleName()).getType());
164       if(o instanceof Titel) {
165         Titel titel = (Titel) o;
166         String titelUrl = titel.getKatalogUrl() + titel.getPfad() + titel.getName();
167         Entity entity = fs.read(FileStorage.ST_ABSPIELER, elems[4]);
168         if (entity instanceof Abspieler) {
169           Abspieler abspieler = (Abspieler) entity;
ad3e2d 170           String server = getEinstellung(fs, getResString(RB_HOST), DEFAULT_HOST);
c6fdc4 171           String signal = abspielKommando(fs, abspieler, server, titelUrl, abspielerKmd).toString();
d027b5 172           abspielerKommandoSenden(signal);
U 173           return signal + "gesendet.";
174         } else {
175           return meldung("Ungueltiger Abspieler.", 404);
176         }
1c5fa4 177       } else {
d027b5 178         return meldung("Ungueltiger Titel.", 404);
1c5fa4 179       }
c6fdc4 180     } else if(elems[6].equalsIgnoreCase("stream")) {
d027b5 181       String streamJson = bodyLesen(e);
U 182       Gson gson = new Gson();
183       Object o = gson.fromJson(streamJson, fs.typeFromName(Livestream.class.getSimpleName()).getType());
184       if(o instanceof Livestream) {
185         Entity entity = fs.read(FileStorage.ST_LIVESTREAM, ((Livestream) o).getName());
186         if(entity instanceof Livestream) {
187           Livestream stream = (Livestream) entity;
188           entity = fs.read(FileStorage.ST_ABSPIELER, elems[4]);
189           if (entity instanceof Abspieler) {
190             Abspieler abspieler = (Abspieler) entity;
191             String server = "";
c6fdc4 192             String signal = abspielKommando(fs, abspieler, server, stream.getUrl(), PL_CMD_PLAY).toString();
d027b5 193             abspielerKommandoSenden(signal);
U 194             return signal + "gesendet.";
195           } else {
196             return meldung("Ungueltiger Abspieler.", 404);
197           }
198         } else {
199           return meldung("Ungueltiger Livestream.", 404); 
200         }
201       } else {
202        return meldung("Ungueltiger Livestream.", 404); 
203       }      
1c5fa4 204     } else {
d027b5 205       return meldung("Ungueltiger URL.", 404);
658c14 206     }
U 207   }
208   
c6fdc4 209   
589850 210   private String kommandoSenden(Storage s, String aName, String kommando) {
U 211     Entity entity = s.read(FileStorage.ST_ABSPIELER, aName);
212     if (entity instanceof Abspieler) {
213       Abspieler abspieler = (Abspieler) entity;
214       StringBuilder kmd = new StringBuilder();
215       kmd.append(abspieler.getUrl());
216       kmd.append(kommando);
9e14ef 217       String signal = kmd.toString();
c6fdc4 218       //String server = getEinstellung(s, App.getRs(App.RB_HOST), DEFAULT_HOST);
U 219       //String signal = abspielKommando(s, abspieler, server, stream.getUrl(), PL_CMD_PLAY).toString();
9e14ef 220       abspielerKommandoSenden(signal);
U 221       return signal + " gesendet.";
589850 222     } else {
U 223       return meldung("Abspielliste nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
224     }
b56bb3 225   }
c6fdc4 226   
b56bb3 227   
5f7e0b 228   private String ersterTitel(Storage s, String aName, String lName) {
U 229     String response;
230     Entity entity = s.read(FileStorage.ST_ABSPIELLISTE, lName);
231     if (entity instanceof Abspielliste) {
232       Abspielliste liste = (Abspielliste) entity;
233       response = listentitelSpielen(s, aName, liste, 0);
234     } else {
235       response = meldung("Abspielliste nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
236     }
237     return response;
0e9cd3 238   }
U 239
240   private String naechsterTitel(Storage s, String abspielerName) {
241     String response;
242     Object o = spielt.get(abspielerName);
243     if (o instanceof Abspielvorgang) {
244       Abspielvorgang av = (Abspielvorgang) o;
245       Entity entity = s.read(FileStorage.ST_ABSPIELLISTE, av.getListe());
246       if (entity instanceof Abspielliste) {
b56bb3 247         Abspielliste liste = (Abspielliste) entity;
0e9cd3 248         int titelNr = av.getTitelNr();
U 249         if (liste.getTitel().size() > ++titelNr) {
005d7a 250           response = listentitelSpielen(s, abspielerName, liste, titelNr);
0e9cd3 251         } else {
U 252           response = "Liste " + liste.getName() + " ist zuende gespielt.";
589850 253           logger.info(response);
0e9cd3 254         }
U 255       } else {
256         response = meldung("Abspielliste nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
b56bb3 257       }
0e9cd3 258       //response = listenTitelSpielen(e, elems[4]);
U 259     } else {
260       response = meldung("Abspielvorgang fuer Abspieler " + abspielerName, AbstractHandler.RTC_NOT_FOUND);
b56bb3 261     }
U 262     return response;
263   }
0e9cd3 264
005d7a 265   private String listentitelSpielen(Storage s, String aName, Abspielliste liste, int titelNr) {
0e9cd3 266     String response;
U 267     Entity entity = s.read(FileStorage.ST_ABSPIELER, aName);
268     if (entity instanceof Abspieler) {
269       Abspieler abspieler = (Abspieler) entity;
270       String kommando = kommandoFuerTitel(s, liste, abspieler, titelNr);
271       //String kommando = kmd.toString();
272       logger.info(kommando);
5f7e0b 273       abspielerKommandoSenden(kommando);
0e9cd3 274       response = "Abspielen der Liste " + liste.getName() + " auf Abspieler " + aName + " gestartet.";
U 275     } else {
276       response = meldung("Abspieler nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
277     }
278     return response;
279   }
280
281   /**
282    * Das Kommando zum Abspielen fuer den Titel einer Abspielliste und einen bestimmten Abspieler
283    * ermitteln.
284    *
285    * @param s die Ablage, in der Abspieler und Abspiellisten zu finden sind
286    * @param liste Name der Liste, die den gewuenschten Titel enthaelt
287    * @param abspieler Name des Abspielers, der zum Abspielen dienen soll
288    * @param titelNr Nummer des Titels in der Liste
289    * @return das Kommando zum Abspielen (ein URL)
290    */
291   private String kommandoFuerTitel(Storage s, Abspielliste liste, Abspieler abspieler, int titelNr) {
292     // ersten Titel aus Liste holen
293     Titel titel = liste.getTitel().get(titelNr);
294
295     // URL des Titels ermitteln
296     String titelUrl = titel.getKatalogUrl() + titel.getPfad() + titel.getName();
297     logger.log(Level.INFO, "abspielen von {0} auf {1}", new Object[]{titelUrl, abspieler.getUrl()});
298
299     // Titel als 'spielt' vermerken
300     Abspielvorgang vorgang = new Abspielvorgang();
301     vorgang.setAbspieler(abspieler.getName());
302     vorgang.setListe(liste.getName());
303     vorgang.setTitelNr(titelNr);
304     spielt.put(abspieler.getName(), vorgang);
005d7a 305     
ad3e2d 306     String server = getEinstellung(s, getResString(RB_HOST), DEFAULT_HOST);
0e9cd3 307
1c5fa4 308     /*
U 309
0e9cd3 310     // Kommando an den Abspieler zusammenbauen
U 311     StringBuilder kmd = new StringBuilder();
312     kmd.append(abspieler.getUrl());
313     kmd.append(PL_CMD_PLAY);
314     // Parameter fuer den Abspieler holen
005d7a 315     kmd.append(getEinstellung(s, App.getRs(App.RB_PLAYERPARAMS), PL_DEFAULT_PARAMS));
U 316     kmd.append(server);
0e9cd3 317     kmd.append(titelUrl);
1c5fa4 318     */
c6fdc4 319     StringBuilder kmd = abspielKommando(s, abspieler, server, titelUrl, PL_CMD_PLAY);
005d7a 320     kmd.append(PL_PARAM_RUECK);
U 321     kmd.append(server);
3a0556 322     if(!server.endsWith(Server.SLASH)) {
U 323       kmd.append(Server.SLASH);
324     }
005d7a 325     kmd.append(PL_API_STRG);
U 326     kmd.append(abspieler.getName());
327     kmd.append("/ende");
0e9cd3 328
U 329     return kmd.toString();
330   }
1c5fa4 331
c6fdc4 332   private StringBuilder abspielKommando(Storage s, Abspieler abspieler, String server, String titelUrl, String abspielKmd) {
1c5fa4 333     
U 334     // Kommando an den Abspieler zusammenbauen
335     StringBuilder kmd = new StringBuilder();
336     kmd.append(abspieler.getUrl());
c6fdc4 337     //kmd.append(PL_CMD_PLAY);
U 338     kmd.append(abspielKmd);
1c5fa4 339     // Parameter fuer den Abspieler holen
ad3e2d 340     kmd.append(getEinstellung(s, getResString(RB_PLAYERPARAMS), PL_DEFAULT_PARAMS));
1c5fa4 341     kmd.append(server);
U 342     kmd.append(titelUrl);
343
344     return kmd;    
345   }
005d7a 346   
U 347   private String getEinstellung(Storage s, String key, String standardWert) {
348     Entity entity = s.read(Einstellung.class.getSimpleName(), key);
349     if (entity instanceof Einstellung) {
350       Einstellung einstellung = (Einstellung) entity;
351       Object o = einstellung.getValue();
352       if(o instanceof String) {
353         return o.toString();
354       } else {
355         return standardWert;
356       }
357     } else {
358       return standardWert;
359     }
360   }
361   
362   private void abspielerKommandoSenden(String kommando) {
363     /*
364       TODO hier evtl. mit mehreren Versuchen ausgleichen, 
365       dass ein einzelner Versuch nicht 'durchkommt'...
366     */
367     logger.info(kommando);
368     try {
369       HttpURLConnection conn = (HttpURLConnection) new URL(kommando).openConnection();
370       conn.setRequestMethod("GET");
371       conn.connect();
372       int status = conn.getResponseCode();
373       String msg = conn.getResponseMessage();
374       logger.log(Level.INFO, "Kommando {0} mit Status {1} {2} gesendet.", new Object[]{kommando, status, msg});
375     } catch(IOException ex) {
376       logger.log(Level.INFO, ex.getMessage(), ex);
377     }
378   }
379   
5f7e0b 380   private String meldung(String text, int code) {
U 381     setReturnCode(code);
382     return text;
383   }
b56bb3 384 }