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