Persoenliche Mediazentrale
ulrich
2021-04-11 658c148be4541e5a7c836de0bd0ee256fee1ad29
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
20 import com.sun.net.httpserver.HttpExchange;
21 import de.uhilger.mediaz.App;
22 import de.uhilger.mediaz.Server;
0e9cd3 23 import de.uhilger.mediaz.entity.Abspielvorgang;
b56bb3 24 import de.uhilger.mediaz.entity.Abspieler;
U 25 import de.uhilger.mediaz.entity.Abspielliste;
0e9cd3 26 import de.uhilger.mediaz.entity.Einstellung;
b56bb3 27 import de.uhilger.mediaz.entity.Entity;
U 28 import de.uhilger.mediaz.entity.Titel;
29 import de.uhilger.mediaz.store.FileStorage;
0e9cd3 30 import de.uhilger.mediaz.store.Storage;
005d7a 31 import java.io.IOException;
U 32 import java.net.HttpURLConnection;
33 import java.net.URL;
b56bb3 34 import java.util.HashMap;
U 35 import java.util.Map;
0e9cd3 36 import java.util.logging.Level;
b56bb3 37 import java.util.logging.Logger;
U 38
39 /**
0e9cd3 40  * Die MediaSteuerung verarbeitet HTTP-Signale zur Steuerung von Media-Operationen wie z.B. dem
U 41  * Spielen einer Abspielliste oder dem Starten oder Stoppen eines Videos auf einem entfernten
42  * Abspielgeraet.
43  *
44  * HTTP GET /mz/api/strg/abspieler/play/liste/[name] 
45  * HTTP GET /mz/api/strg/abspieler/ende
46  *
47  * HTTP GET /mz/api/strg/abspieler/play/[url]
48  *
589850 49  * HTTP GET /mz/api/strg/abspieler/pause 
U 50  * HTTP GET /mz/api/strg/abspieler/stop 
51  * HTTP GET /mz/api/strg/abspieler/weiter
b56bb3 52  * 
0e9cd3 53  * Faustregel: Anzahl Elemente eines URL plus 1 ist die Anzahl der Elemente des 
U 54  * Ergebnisses von String.split.
55  *
56  *
b56bb3 57  * @author Ulrich Hilger
U 58  * @version 1, 9.4.2021
59  */
60 public class MediaSteuerung extends AbstractHandler {
61
62   private static final Logger logger = Logger.getLogger(MediaSteuerung.class.getName());
0e9cd3 63
U 64   public static final String PL_CMD_PLAY = "avd/play";
65   public static final String PL_DEFAULT_PARAMS = "?titel=";
005d7a 66   public static final String PL_PARAM_RUECK = "&r=";
U 67   public static final String PL_API_STRG = "/api/strg/"; 
0e9cd3 68   public static final String PL_CMD_ENDE = "ende";
589850 69   public static final String PL_CMD_STOP = "stop";
U 70   public static final String PL_CMD_PAUSE = "pause";
71   public static final String PL_CMD_PLAYON = "playon";
72   public static final String PL_CMD_AVD_STOP = "avd/stop";
73   public static final String PL_CMD_AVD_PAUSE = "avd/pause";
74   public static final String PL_CMD_AVD_PLAYON = "avd/playon";
005d7a 75   public static final String DEFAULT_HOST = "http://localhost:9090";
0e9cd3 76
U 77   private final Map spielt = new HashMap();
b56bb3 78
U 79   @Override
80   protected String get(HttpExchange e) {
0e9cd3 81     String response;
b56bb3 82     String path = e.getRequestURI().toString();
0e9cd3 83     String[] elems = path.split(Server.SLASH);
U 84     FileStorage fs = new FileStorage(App.getInitParameter(App.getRs(App.RB_AP_CONF)));
589850 85     logger.fine(path);
0e9cd3 86     
U 87     // Faustregel: Anzahl Elemente eines URL plus 1 ist die Anzahl der Elemente des 
88     // Ergebnisses von String.split.
89     switch (elems.length) {
90       case 6:
91         if (elems[5].equalsIgnoreCase(PL_CMD_ENDE)) {
92           response = naechsterTitel(fs, elems[4]);
589850 93         } else if(elems[5].equalsIgnoreCase(PL_CMD_STOP)) {
d12f6e 94           spielt.remove(elems[4]);
589850 95           response = kommandoSenden(fs, elems[4], PL_CMD_AVD_STOP);
U 96         } else if(elems[5].equalsIgnoreCase(PL_CMD_PAUSE)) {
97           response = kommandoSenden(fs, elems[4], PL_CMD_AVD_PAUSE);
98         } else if(elems[5].equalsIgnoreCase(PL_CMD_PLAYON)) {
99           response = kommandoSenden(fs, elems[4], PL_CMD_AVD_PLAYON);
0e9cd3 100         } else {
U 101           response = meldung("Ungueltiges Kommando: " + elems[5], AbstractHandler.RTC_NOT_FOUND);
102         }
103         break;
b56bb3 104       case 8:
658c14 105         if(elems[6].equalsIgnoreCase("liste")) {
U 106           response = ersterTitel(fs, elems[4], elems[7]);
107         } else {
108           response = urlAbspielen(fs, elems);
109         }
0e9cd3 110         break;
U 111       default:
112         response = "Ungueltiger URL";
b56bb3 113         break;
U 114     }
115     return response;
589850 116   }
U 117   
658c14 118   private String urlAbspielen(Storage s, String[] elems) {
U 119     String abspielerName = elems[4];
120     StringBuilder url = new StringBuilder();
121     url.append(url)
122     for(int i = 6; i < elems.length - 1; i++) {
123       url.append(Server.SLASH);
124       url.append(elems[i]);
125     }
126   }
127   
589850 128   private String kommandoSenden(Storage s, String aName, String kommando) {
U 129     Entity entity = s.read(FileStorage.ST_ABSPIELER, aName);
130     if (entity instanceof Abspieler) {
131       Abspieler abspieler = (Abspieler) entity;
132       StringBuilder kmd = new StringBuilder();
133       kmd.append(abspieler.getUrl());
134       kmd.append(kommando);
9e14ef 135       String signal = kmd.toString();
U 136       abspielerKommandoSenden(signal);
137       return signal + " gesendet.";
589850 138     } else {
U 139       return meldung("Abspielliste nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
140     }
b56bb3 141   }
U 142   
5f7e0b 143   private String ersterTitel(Storage s, String aName, String lName) {
U 144     String response;
145     Entity entity = s.read(FileStorage.ST_ABSPIELLISTE, lName);
146     if (entity instanceof Abspielliste) {
147       Abspielliste liste = (Abspielliste) entity;
148       response = listentitelSpielen(s, aName, liste, 0);
149     } else {
150       response = meldung("Abspielliste nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
151     }
152     return response;
0e9cd3 153   }
U 154
155   private String naechsterTitel(Storage s, String abspielerName) {
156     String response;
157     Object o = spielt.get(abspielerName);
158     if (o instanceof Abspielvorgang) {
159       Abspielvorgang av = (Abspielvorgang) o;
160       Entity entity = s.read(FileStorage.ST_ABSPIELLISTE, av.getListe());
161       if (entity instanceof Abspielliste) {
b56bb3 162         Abspielliste liste = (Abspielliste) entity;
0e9cd3 163         int titelNr = av.getTitelNr();
U 164         if (liste.getTitel().size() > ++titelNr) {
005d7a 165           response = listentitelSpielen(s, abspielerName, liste, titelNr);
0e9cd3 166         } else {
U 167           response = "Liste " + liste.getName() + " ist zuende gespielt.";
589850 168           logger.info(response);
0e9cd3 169         }
U 170       } else {
171         response = meldung("Abspielliste nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
b56bb3 172       }
0e9cd3 173       //response = listenTitelSpielen(e, elems[4]);
U 174     } else {
175       response = meldung("Abspielvorgang fuer Abspieler " + abspielerName, AbstractHandler.RTC_NOT_FOUND);
b56bb3 176     }
U 177     return response;
178   }
0e9cd3 179
005d7a 180   private String listentitelSpielen(Storage s, String aName, Abspielliste liste, int titelNr) {
0e9cd3 181     String response;
U 182     Entity entity = s.read(FileStorage.ST_ABSPIELER, aName);
183     if (entity instanceof Abspieler) {
184       Abspieler abspieler = (Abspieler) entity;
185       String kommando = kommandoFuerTitel(s, liste, abspieler, titelNr);
186       //String kommando = kmd.toString();
187       logger.info(kommando);
5f7e0b 188       abspielerKommandoSenden(kommando);
0e9cd3 189       response = "Abspielen der Liste " + liste.getName() + " auf Abspieler " + aName + " gestartet.";
U 190     } else {
191       response = meldung("Abspieler nicht gefunden.", AbstractHandler.RTC_NOT_FOUND);
192     }
193     return response;
194   }
195
196   /**
197    * Das Kommando zum Abspielen fuer den Titel einer Abspielliste und einen bestimmten Abspieler
198    * ermitteln.
199    *
200    * @param s die Ablage, in der Abspieler und Abspiellisten zu finden sind
201    * @param liste Name der Liste, die den gewuenschten Titel enthaelt
202    * @param abspieler Name des Abspielers, der zum Abspielen dienen soll
203    * @param titelNr Nummer des Titels in der Liste
204    * @return das Kommando zum Abspielen (ein URL)
205    */
206   private String kommandoFuerTitel(Storage s, Abspielliste liste, Abspieler abspieler, int titelNr) {
207     // ersten Titel aus Liste holen
208     Titel titel = liste.getTitel().get(titelNr);
209
210     // URL des Titels ermitteln
211     String titelUrl = titel.getKatalogUrl() + titel.getPfad() + titel.getName();
212     logger.log(Level.INFO, "abspielen von {0} auf {1}", new Object[]{titelUrl, abspieler.getUrl()});
213
214     // Titel als 'spielt' vermerken
215     Abspielvorgang vorgang = new Abspielvorgang();
216     vorgang.setAbspieler(abspieler.getName());
217     vorgang.setListe(liste.getName());
218     vorgang.setTitelNr(titelNr);
219     spielt.put(abspieler.getName(), vorgang);
005d7a 220     
U 221     String server = getEinstellung(s, App.getRs(App.RB_HOST), DEFAULT_HOST);
0e9cd3 222
U 223     // Kommando an den Abspieler zusammenbauen
224     StringBuilder kmd = new StringBuilder();
225     kmd.append(abspieler.getUrl());
226     kmd.append(PL_CMD_PLAY);
227     // Parameter fuer den Abspieler holen
005d7a 228     kmd.append(getEinstellung(s, App.getRs(App.RB_PLAYERPARAMS), PL_DEFAULT_PARAMS));
U 229     kmd.append(server);
0e9cd3 230     kmd.append(titelUrl);
005d7a 231     kmd.append(PL_PARAM_RUECK);
U 232     kmd.append(server);
233     kmd.append(PL_API_STRG);
234     kmd.append(abspieler.getName());
235     kmd.append("/ende");
0e9cd3 236
U 237     return kmd.toString();
238   }
005d7a 239   
U 240   private String getEinstellung(Storage s, String key, String standardWert) {
241     Entity entity = s.read(Einstellung.class.getSimpleName(), key);
242     if (entity instanceof Einstellung) {
243       Einstellung einstellung = (Einstellung) entity;
244       Object o = einstellung.getValue();
245       if(o instanceof String) {
246         return o.toString();
247       } else {
248         return standardWert;
249       }
250     } else {
251       return standardWert;
252     }
253   }
254   
255   private void abspielerKommandoSenden(String kommando) {
256     /*
257       TODO hier evtl. mit mehreren Versuchen ausgleichen, 
258       dass ein einzelner Versuch nicht 'durchkommt'...
259     */
260     logger.info(kommando);
261     try {
262       HttpURLConnection conn = (HttpURLConnection) new URL(kommando).openConnection();
263       conn.setRequestMethod("GET");
264       conn.connect();
265       int status = conn.getResponseCode();
266       String msg = conn.getResponseMessage();
267       logger.log(Level.INFO, "Kommando {0} mit Status {1} {2} gesendet.", new Object[]{kommando, status, msg});
268     } catch(IOException ex) {
269       logger.log(Level.INFO, ex.getMessage(), ex);
270     }
271   }
272   
5f7e0b 273   private String meldung(String text, int code) {
U 274     setReturnCode(code);
275     return text;
276   }
277   
b56bb3 278   // rpi4-az:9090/avd/play?titel=/Filme/S/sound_city.m4v&th=60&ti=60&o=local
U 279   // aUrl http://rpi4-wz:9090/
280   // titelUrl /media/test/A/The-Alan-Parsons-Project/I-Robot/02-I-Wouldnt-Want-to-Be-Like-You.mp3
281 }