Persoenliche Mediazentrale
ulrich
2024-11-22 0bdb112dcff891a85f076774f472a244c68dcbad
commit | author | age
86bbf7 1 /*
94b1c2 2   Tango - Personal Media Center
cf6509 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/>.
86bbf7 17  */
94b1c2 18 package de.uhilger.tango.api;
86bbf7 19
095119 20 import com.sun.net.httpserver.Headers;
86bbf7 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.Einstellung;
25 import de.uhilger.tango.entity.Entity;
26 import de.uhilger.tango.store.FileStorage;
27 import de.uhilger.tango.store.Storage;
28 import de.uhilger.tango.store.StorageFile;
86bbf7 29 import java.io.File;
be4056 30 import java.io.FileFilter;
86bbf7 31 import java.io.IOException;
U 32 import java.io.OutputStream;
33 import java.util.ArrayList;
d4d091 34 import java.util.Arrays;
dce2c7 35 import java.util.HashMap;
U 36 import java.util.Map;
be4056 37 import java.util.Set;
dce2c7 38 import java.util.logging.Level;
86bbf7 39 import java.util.logging.Logger;
a48ca3 40 import org.farng.mp3.MP3File;
U 41 import org.farng.mp3.TagException;
ea73fa 42 import org.farng.mp3.id3.AbstractID3v2;
a48ca3 43 import org.farng.mp3.id3.ID3v1;
86bbf7 44
U 45 /**
1ff360 46  * Die Klasse ListFileHandler gibt die Media-Inhalte eines Ablageortes 
U 47  * als Liste aus. Audio-Inhalte werden dabei unter Verwendung von 
48  * Informationen aus ID3-Tags dargestellt, z.B. Artist, Album, Titel, 
49  * sofern solche vorhanden sind. Zudem werden nur diejenigen Inahlte 
50  * ausgegeben, die Dateierweiterungen besitzen, wie sie in der FileStorage 
51  * unter 'Einstellung' unter audioexts, videoexts und imageexts angegeben 
52  * sind (die Namen der Einstellungen sind ueber das Resource Bundle von 
53  * Tango ueber RB_AUDIOEXTS, RB_VIDEOEXTS und RB_FOTOEXTS veraenderbar).
54  * 
55  * Ein ListFileHandler gibt dann fuer Ordner den Inhalt als Liste aus und 
56  * Streamt den Inhalt von Dateien.
57  * 
58  * Der ListFileHandler modelliert das Verhalten, das auf der Bedienoberflaeche 
59  * von Tango in der Konfiguration als 'Kataloge' angelegt werden kann. Die 
60  * HTTP Servicepunkte ergeben sich aus den Ablageort-Objekten die fuer 
61  * Kataloge vom Benutzer in der Konfiguration angelegt werden.
62  * 
63  * Der vom Benutzer gewaehlte URL eines Ablagortes wird vom ListFileHandler zu 
64  * dem Pfad des Ablageortes hin verknuepft. So ergibt sich fuer jeden 
65  * ListFileHandler der Endpunkt
66  * 
67  * HTTP GET http://mein-server/tango/[Ablageort-URL]/
68  * 
69  * Ist z.B. Audio und Video unter dem Pfad /media/extssd/mc abgelegt, kann 
70  * ein Ablageort namens 'AV' den URL /media fuer diesen Pfad definieren. Dann verweist
71  * 
72  * http://mein-server/tango/media/
73  * 
74  * auf den Inhalt unter /media/extssd/mc
75  * 
76  * Hierbei wird der Inhalt unter dem Katalognamen 'AV' in der Bedienoberflaeche von 
77  * Tango dargestellt, d.h., die Auswahl von 'AV' an der Bedienoberflaeche bewirkt 
78  * 'unter der Haube' den Abruf von http://mein-server/tango/media/
79  * 
80  * Selbstverstaendlich kann aber dieser URL auch von ueberallher verwendet werden. 
81  * Die Verknuepfung zwischen Katalogname und URL besteht nur an der Bedienoberflaeche 
82  * von Tango.
83  * 
84  * @author Ulrich Hilger
86bbf7 85  */
U 86 public class ListFileHandler extends FileHandler {
87   
ad3e2d 88   public static final String RB_AUDIOEXTS = "audioexts";
U 89   public static final String RB_VIDEOEXTS = "videoexts";
a1333d 90   public static final String RB_FOTOEXTS = "imageexts";
ad3e2d 91   
86bbf7 92   /* Der Logger fuer diesen ListFileHandler */
U 93   private static final Logger logger = Logger.getLogger(ListFileHandler.class.getName());
94   
630b44 95   private static final String[] specialChars = {new String("\u00c4"), new String("\u00d6"), 
U 96       new String("\u00dc"), new String("\u00e4"), new String("\u00f6"), new String("\u00fc"), new String("\u00df")};
ea73fa 97   
U 98   public static final String UNWANTED_PATTERN = "[^a-zA-Z_0-9 ]";
630b44 99   
dce2c7 100   Map extMap = new HashMap();
U 101   
f70acb 102   private String conf;
U 103   
104   public ListFileHandler(String absoluteDirectoryPathAndName, String conf) {
86bbf7 105     super(absoluteDirectoryPathAndName);
dce2c7 106     /*
0e9cd3 107       Ermittlung von Dateifiltern. 
dce2c7 108       Sie werden erwartet in den Einstellungen 'audioexts' und 'videoexts'
U 109       jeweils als Dateierweiterungen mit Komma getrennt
110       z.B. "mp4,m4v"
111     */
f70acb 112     FileStorage fs = new FileStorage(conf);
ad3e2d 113     initMap(fs, getResString(RB_AUDIOEXTS), StorageFile.TYP_AUDIO);
U 114     initMap(fs, getResString(RB_VIDEOEXTS), StorageFile.TYP_VIDEO);
a1333d 115     initMap(fs, getResString(RB_FOTOEXTS), StorageFile.TYP_FOTO);
86bbf7 116   }
U 117
dce2c7 118   private void initMap(Storage s, String key, String typ) {
U 119     Entity e = s.read(Einstellung.class.getSimpleName(), key);
120     if(e instanceof Einstellung) {
121       String[] exts = ((Einstellung) e).getValue().split(",");
122       for(String ext : exts) {
123         extMap.put(ext, typ);
124       }
125     }
126   }
127   
86bbf7 128   @Override
U 129   public void handle(HttpExchange e) throws IOException {
130     String path = e.getRequestURI().toString();
131     logger.fine(path);
0e9cd3 132     if(path.endsWith(Server.SLASH)) {
86bbf7 133       String fName = getFileName(e);
U 134       logger.fine(fName);
135       File dir = new File(fileBase, fName);
136       logger.fine(dir.getAbsolutePath());
be4056 137       File[] files = dir.listFiles(new FileFilter() {
U 138         @Override
139         public boolean accept(File pathname) {
140           Set keys = extMap.keySet();
141           String fname = pathname.getName();
142           int pos = fname.lastIndexOf(".");
143           String ext = fname.substring(pos+1);          
144           return keys.contains(ext) || pathname.isDirectory();
145         }
146       });
d4d091 147       Arrays.sort(files);      
86bbf7 148       ArrayList list = new ArrayList();
U 149       if(files != null) {
150         for(File file : files) {
151           StorageFile sf = new StorageFile();
7c22a2 152           String fname = file.getName();
U 153           sf.setName(fname);
b56bb3 154           sf.setTitelAnzName(fname);
86bbf7 155           if(file.isDirectory()) {
822ddf 156             sf.setTyp(StorageFile.TYP_FOLDER);
86bbf7 157           } else {
dce2c7 158             int pos = fname.lastIndexOf(".");
U 159             String ext = fname.substring(pos+1);
160             logger.log(Level.FINE, "ext: {0}", ext);
161             Object o = extMap.get(ext);
162             if(o instanceof String) {
163               sf.setTyp(o.toString());
a48ca3 164               getMetadata(file, sf);
7c22a2 165             } else {
U 166               sf.setTyp(StorageFile.TYP_FILE);
167             }
86bbf7 168           }
U 169           list.add(sf);
170         }
171       }
d4d091 172       //Collections.sort(list);
ea73fa 173       String rawjson = jsonWithCustomType(list, "Medialiste");
U 174       String json = escapeHtml(rawjson);
630b44 175       
86bbf7 176       logger.fine(json);
095119 177       Headers headers = e.getResponseHeaders();
630b44 178       headers.add("Content-Type", "application/json; charset=UTF-8");
86bbf7 179       e.sendResponseHeaders(200, json.length());
U 180       OutputStream os = e.getResponseBody();
181       os.write(json.getBytes());
182       os.close();        
183     } else {
184       super.handle(e);
185     }
186   }
187   
630b44 188   public String escapeHtml(String text) {
U 189     text = text.replace(specialChars[0], "Ae");
190     text = text.replace(specialChars[1], "Oe");
191     text = text.replace(specialChars[2], "Ue");
192     text = text.replace(specialChars[3], "ae");
193     text = text.replace(specialChars[4], "oe");
194     text = text.replace(specialChars[5], "ue");
195     text = text.replace(specialChars[6], "ss");
196     
197     /*
198     text = text.replace(specialChars[0], "&Auml;");
199     text = text.replace(specialChars[1], "&Ouml;");
200     text = text.replace(specialChars[2], "&Uuml;");
201     text = text.replace(specialChars[3], "&auml;");
202     text = text.replace(specialChars[4], "&ouml;");
203     text = text.replace(specialChars[5], "&uuml;");
204     text = text.replace(specialChars[6], "&szlig;");
205     */
206     return text;
207   }
208   
a48ca3 209   private void getMetadata(File file, StorageFile sf) {
37eadf 210     if(sf.getTyp().equalsIgnoreCase(StorageFile.TYP_AUDIO)) {
a48ca3 211       try {
U 212         MP3File mp3 = new MP3File(file);
213         ID3v1 tag = mp3.getID3v1Tag();
ea73fa 214         if(tag == null) {
U 215           AbstractID3v2 tag2 = mp3.getID3v2Tag();
216           sf.setInterpret(tag2.getLeadArtist().replaceAll(UNWANTED_PATTERN, ""));
217           String trackTitel = tag2.getSongTitle().replaceAll(UNWANTED_PATTERN, "");
218           if(trackTitel != null && trackTitel.length() > 0) {
219             sf.setTitelAnzName(trackTitel);
220           }
221           sf.setAlbum(tag2.getAlbumTitle().replaceAll(UNWANTED_PATTERN, ""));
222         } else {
223           sf.setInterpret(tag.getArtist().replaceAll(UNWANTED_PATTERN, ""));
224           String trackTitel = tag.getTitle().replaceAll(UNWANTED_PATTERN, "");
225           if(trackTitel != null && trackTitel.length() > 0) {
226             sf.setTitelAnzName(trackTitel);
227           }
228           sf.setAlbum(tag.getAlbumTitle().replaceAll(UNWANTED_PATTERN, ""));
a48ca3 229         }
U 230       } catch (IOException ex) {
231         Logger.getLogger(ListFileHandler.class.getName()).log(Level.SEVERE, null, ex);
232       } catch (TagException ex) {
233         Logger.getLogger(ListFileHandler.class.getName()).log(Level.SEVERE, null, ex);
b56bb3 234       }
37eadf 235     }
U 236   }
86bbf7 237   
U 238 }