Persoenliche Mediazentrale
undisclosed
2023-01-11 5bf530d39a7e06bdd982a5c2495ff9a04fd83da4
commit | author | age
1ff360 1 package de.uhilger.tango.api;
U 2
5bf530 3 import com.sun.net.httpserver.Headers;
U 4 import com.sun.net.httpserver.HttpExchange;
5 import de.uhilger.tango.App;
6 import de.uhilger.tango.Server;
7 import static de.uhilger.tango.api.FileHandler.CONTENT_LENGTH;
8 import static de.uhilger.tango.api.FileHandler.HTTP_GET;
9 import static de.uhilger.tango.api.FileHandler.RANGE_HEADER;
10 import static de.uhilger.tango.api.FileHandler.RB_NOT_FOUND;
11 import static de.uhilger.tango.api.FileHandler.RB_WELCOME_FILE;
12 import static de.uhilger.tango.api.FileHandler.SC_NOT_FOUND;
13 import static de.uhilger.tango.api.FileHandler.SC_OK;
14 import static de.uhilger.tango.api.FileHandler.STR_BLANK;
15 import static de.uhilger.tango.api.FileHandler.STR_DOT;
16 import de.uhilger.tango.entity.Ablageort;
17 import de.uhilger.tango.entity.Abspielliste;
18 import de.uhilger.tango.entity.Entity;
19 import de.uhilger.tango.entity.Titel;
20 import de.uhilger.tango.store.FileStorage;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.nio.charset.StandardCharsets;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.ResourceBundle;
33 import java.util.logging.Level;
34 import java.util.logging.Logger;
35
1ff360 36 /**
5bf530 37  * Der StreamHandler liefert ganze Abspiellisten als einzelnen Stream aus. Die in Tango mit dem
U 38  * ListHandler erstellen Abspiellisten werden als ein zusammenhaengender Stream ausgegeben.
39  *
40  * Den Inhalt einer Abspielliste als Stream ausgeben HTTP GET /tango/api/stream/liste/[name]
41  *
42  *
43  *
44  *
45  * die folgenden URLs ggf. wieder wegnehmen
46  *
47  * HTTP GET /tango/api/stream/play/liste/[name]
48  *
49  * HTTP GET /tango/api/stream/pause/liste/[name] HTTP GET /tango/api/stream/stop/liste/[name] HTTP
50  * GET /tango/api/stream/seek/liste/[name]/[sekunden]
51  *
52  * Die Funktionen des StreamHandlers ergaenzen so die Ausgabe einzelner Media-Dateien als Stream,
53  * wie sie mit dem FileHandler und seinen Subklassen sowie mit der MediaSteuerung erfolgen.
54  *
1ff360 55  * @author Ulrich Hilger
U 56  */
5bf530 57 public class StreamHandler extends FileHandler {
U 58   private static final Logger logger = Logger.getLogger(StreamHandler.class.getName());
59
60   public static final String PLAY = "play";
61   public static final String LISTE = "liste";
62
63   private HashMap listen;
64   private HashMap<String, File> files;
65   private HashMap<String, Long> bytes;
66   private HashMap<String, String> kataloge;
67   private String conf;
68
69   public StreamHandler(String conf) {
70     super(""); // wird spaeter gesetzt
71     listen = new HashMap<String, Integer>(); // abspiellistenname -> z.Zt. spielender Index
72     kataloge = new HashMap(); // url -> abs. pfad
73     files = new HashMap<String, File>(); // abspiellistenname -> z zt spielende datei
74     bytes = new HashMap<String, Long>(); // abspiellistenname -> gespielte bytes
75     this.conf = conf;
76     ablageorteLesen();
77   }
78
79   @Override
80   public void handle(HttpExchange e) throws IOException {
81     String path = e.getRequestURI().toString();
82     String[] elems = path.split(Server.SLASH);
83     String lName = elems[5];
84
85     Integer index;
86     File file;
87     FileStorage s = new FileStorage(conf);
88     Object o = listen.get(lName);
89     if (o instanceof Integer) {
90       // liste spielt schon
91       index = (Integer) o;
92       file = files.get(lName);
93     } else {
94       index = 0;
95       // liste spielt noch nicht
96       listen.put(lName, index);
97       file = getFileToPlay(s, lName, index.intValue());
98       files.put(lName, file);
99       bytes.put(lName, Long.valueOf(0));
100     }
101
102     //String fName = getFileName(e);
103     String fName = file.getName();
104     if (fName.startsWith(STR_DOT)) {
105       sendNotFound(e, fName);
106     } else {
107       Headers headers = e.getRequestHeaders();
108       int indexVal = index.intValue();
109       if (headers.containsKey(RANGE_HEADER)) {
110         logger.info("range header present, serving list file parts");
111         serveListParts(e, s, lName, file, indexVal);
112         //serveList(e, s, lName, file, indexVal);
113       } else {
114         //if (fName.length() < 1 || fName.endsWith(Server.SLASH)) {
115         //  ResourceBundle rb = ResourceBundle.getBundle(App.RB_NAME);
116         //  fName += getResString(RB_WELCOME_FILE);
117         //}
118         
119         logger.info("no range header or header ignored, streaming whole files");
120         serveList(e, s, lName, file, indexVal);
121         //while(file != null) {
122         //  files.put(lName, file);
123         //  listen.put(lName, index);
124         //  serveFile(e, file);
125         //  file = getFileToPlay(s, lName, ++indexVal);
126         //}
127       }
128     }
129     
130     /*
131     String response = lName;
132     Headers headers = e.getResponseHeaders();
133     headers.add("Content-Type", "application/json");
134     e.sendResponseHeaders(200, response.length());
135     OutputStream os = e.getResponseBody();
136     os.write(response.getBytes());
137     os.close();
138      */
139   }
1ff360 140   
5bf530 141   private void serveListParts(HttpExchange e, FileStorage s, String lName, File file, int indexVal) throws IOException {
U 142     if (file.exists()) {
143       setHeaders(e, file);
144       //e.getResponseHeaders().set(CONTENT_LENGTH, Long.toString(Long.MAX_VALUE));
145       //e.sendResponseHeaders(SC_OK, Long.MAX_VALUE);
146       //e.getResponseHeaders().set(CONTENT_LENGTH, Long.toString(Long.MAX_VALUE));
147       //e.sendResponseHeaders(SC_OK, Long.MAX_VALUE);
148       //e.sendResponseHeaders(SC_PARTIAL_CONTENT, Long.MAX_VALUE);
149       logger.info("playing " + file.getName());
150       logger.info("file length: " + file.length());
151       InputStream is = new FileInputStream(file);
152       OutputStream os = e.getResponseBody();
153       while (file != null) {     
154         //if(is instanceof InputStream) {
155         //  is.close();
156         //}
157         //is = new FileInputStream(file);
158         files.put(lName, file);
159         listen.put(lName, indexVal);
160         file = serveFileParts(e, file, is, os, s, lName, indexVal);
161         //serveFile(e, file);
162         if(bytes.get(lName) == 0l) {
163           file = getFileToPlay(s, lName, ++indexVal);
164           if(is instanceof InputStream) {
165             is.close();
166           }
167           is = new FileInputStream(file);    
168           logger.info("file length: " + file.length());
169         }
170         logger.info("playing " + file.getName());
171       }
172       //os.flush();
173       if(is instanceof InputStream) {
174         is.close();
175       }
176       logger.info("fertig os flush und close ");
177       os.flush();
178       os.close();
179     } else {
180       sendNotFound(e, file.getName());
181     }
182     logger.info("ende");
183   }
184   
185   
186   
187   protected File serveFileParts(HttpExchange e, File file, InputStream is, OutputStream os, FileStorage s, String lName, int indexVal) throws IOException {
188     if (file.exists()) {
189       setHeaders(e, file);
190       //Long byteCount = bytes.get(lName);
191       //logger.info("byteCount at start: " + byteCount);
192       long responseLength = 0;
193       long start = 0;
194       long end;
195       String hdr;
196       RangeGroup rangeGroup = parseRanges(e, file);
197       Iterator<Range> i = rangeGroup.getRanges();
198       Headers resHeaders = e.getResponseHeaders();
199       while (i.hasNext()) {
200         Range range = i.next();
201         start = range.getStart();
202         //start = 0;
203         end = range.getEnd();
204         //end = file.length();
205         //responseLength += (end - start);
206         //start = 0;
207         //end = responseLength;
208         //range.setStart(start);
209         //range.setEnd(end);
210         
211         hdr = contentRangeHdr(range, file);
212         resHeaders.add(CONTENT_RANGE_HEADER, hdr);
213         logger.info("added header " + hdr);
214         responseLength += (end - start);
215       }
216       logger.info("responseLength: " + responseLength);
217       e.sendResponseHeaders(SC_PARTIAL_CONTENT, responseLength);
218       //e.sendResponseHeaders(SC_PARTIAL_CONTENT, Long.MAX_VALUE);
219       //logger.info("responseHeaders gesendet ");
220       //if(HTTP_GET.equalsIgnoreCase(e.getRequestMethod())) {
221         //InputStream is = new FileInputStream(file);
222         //OutputStream os = e.getResponseBody();
223         //long streamPos = bytes.get(lName);
224         long count = 0;
225         if (start > 0) {
226           logger.info("skip to " + start);
227           is.skip(start);
228         }
229         int byteRead = is.read();
230         logger.info("starte while mit count=" + count);
231         while (/*byteRead > -1 && */ count < responseLength) {
232           ++count;
233           //++streamPos;
234           os.write(byteRead);
235           byteRead = is.read();
236           //logger.info("byteRead " + byteRead);
237           if(byteRead < 0 && count < responseLength) {
238             logger.info("dateiende, naechste Datei");
239             
240             file = getFileToPlay(s, lName, ++indexVal);
241             if(file != null) {
242               logger.info("playing " + file.getName());
243               //streamPos = 0;
244               is.close();
245               is = new FileInputStream(file);
246               byteRead = is.read();
247               logger.info("neue Datei, count " + count + " responseLength " + responseLength);
248               logger.info("file length: " + file.length());
249             } else {
250               //logger.info("Liste zuende");
251               count = Long.MAX_VALUE;         
252               logger.info("Liste zuende");
253             }
254           }
255         }
256         logger.info("while ende, count " + count);
257         //if(streamPos != responseLength) {
258         //  bytes.put(lName, streamPos);
259         //  logger.info("streamPos " + streamPos);
260         //} else {
261         //  bytes.put(lName, Long.valueOf(0));
262         //  logger.info("streamPos " + 0);
263         //}
264         //byteCount = count;
265         
266         //logger.info("byteCount at end: " + byteCount);
267         //os.flush();
268         //os.close();
269         // is.close();
270       }
271     //} else {
272     //  sendNotFound(e, file.getName());
273     //}
274     logger.info("ende");
275     return file;
276   }
277   
278   protected String contentRangeHdr(Range range, File file) {
279     StringBuilder sb = new StringBuilder();
280     sb.append(getResString(RB_BYTES));
281     sb.append(STR_BLANK);
282     sb.append(range.getStart());
283     sb.append(getResString(RB_DASH));
284     sb.append(range.getEnd());
285     sb.append(Server.SLASH);
286     //sb.append(file.length());
287     sb.append(Long.MAX_VALUE);
288     return sb.toString();
289   }
290
291   
292   
293   private void serveList(HttpExchange e, FileStorage s, String lName, File file, int indexVal) throws IOException {
294     if (file.exists()) {
295       setHeaders(e, file);
296       e.getResponseHeaders().set(CONTENT_LENGTH, Long.toString(Long.MAX_VALUE));
297       //e.sendResponseHeaders(SC_OK, Long.MAX_VALUE);
298       InputStream in = new FileInputStream(file);
299       OutputStream out = e.getResponseBody();
300       while (file != null) {
301         files.put(lName, file);
302         listen.put(lName, indexVal);
303         serveFile(e, file, in, out);
304         file = getFileToPlay(s, lName, ++indexVal);
305       }
306       out.flush();
307       out.close();
308       in.close();
309     } else {
310       sendNotFound(e, file.getName());
311     }
312   }
313   
314   /**
315    * Den Inhalt einer Datei ausliefern
316    *
317    * @param e das Objekt mit Methoden zur Untersuchung der Anfrage sowie zum
318    * Anfertigen und Senden der Antwort
319    * @param file die Datei, deren Inhalt ausgeliefert werden soll
320    * @throws IOException falls etwas schief geht entsteht dieser Fehler
321    */
322   protected void serveFile(HttpExchange e, File file, InputStream in, OutputStream out) throws IOException {
323     logger.info("serving file " + file.getName());
324       //if(HTTP_GET.equalsIgnoreCase(e.getRequestMethod())) {
325         int b = in.read();
326         while (b > -1) {
327           out.write(b);
328           b = in.read();
329         }
330         logger.info("done serving file " + file.getName());
331         //in.close();
332         //out.flush();
333       //}
334   }
335
336   
337
338   private File getFileToPlay(FileStorage s, String lName, int index) {
339     Entity entity = s.read(FileStorage.ST_ABSPIELLISTE, lName);
340     if (entity instanceof Abspielliste) {
341       Abspielliste liste = (Abspielliste) entity;
342       List<Titel> titelListe = liste.getTitel();
343       if(titelListe.size() > index) {
344         Titel t = titelListe.get(index);
345         String katalogUrl = t.getKatalogUrl();
346         String pfad = kataloge.get(katalogUrl);
347         return new File(pfad + t.getPfad(), t.getName());
348       } else {
349         return null;
350       }
351     } else {
352       // keine Abspielliste
353       return null;
354     }
355   }
356
357   private void ablageorteLesen() {
358     String typ = Ablageort.class.getSimpleName();
359     FileStorage store = new FileStorage(conf);
360     List<String> orte = store.list(typ);
361     Iterator<String> i = orte.iterator();
362     while (i.hasNext()) {
363       String ortName = i.next();
364       Entity e = store.read(typ, ortName);
365       if (e instanceof Ablageort) {
366         Ablageort ablageort = (Ablageort) e;
367         Logger logger = Logger.getLogger(StreamHandler.class.getName());
368         //logger.log(Level.FINE, "{0}{1}", new Object[]{ctx, ablageort.getUrl()});
369         logger.fine(ablageort.getOrt() + " " + ablageort.getUrl());
370         kataloge.put(ablageort.getUrl(), ablageort.getOrt());
371         //server.createContext(ctx + ablageort.getUrl(),  
372         //new ListFileHandler(new File(ablageort.getOrt()).getAbsolutePath(), conf));
373       }
374     }
375   }
376
1ff360 377 }