/*
|
Tango - Personal Media Center
|
Copyright (C) 2021 Ulrich Hilger
|
|
This program is free software: you can redistribute it and/or modify
|
it under the terms of the GNU Affero General Public License as
|
published by the Free Software Foundation, either version 3 of the
|
License, or (at your option) any later version.
|
|
This program is distributed in the hope that it will be useful,
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
GNU Affero General Public License for more details.
|
|
You should have received a copy of the GNU Affero General Public License
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
*/
|
package de.uhilger.tango.api;
|
|
import com.sun.net.httpserver.Headers;
|
import com.sun.net.httpserver.HttpExchange;
|
import de.uhilger.tango.App;
|
import de.uhilger.tango.Server;
|
import de.uhilger.tango.entity.Einstellung;
|
import de.uhilger.tango.entity.Entity;
|
import de.uhilger.tango.store.FileStorage;
|
import de.uhilger.tango.store.Storage;
|
import de.uhilger.tango.store.StorageFile;
|
import java.io.File;
|
import java.io.FileFilter;
|
import java.io.IOException;
|
import java.io.OutputStream;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.HashMap;
|
import java.util.Map;
|
import java.util.Set;
|
import java.util.logging.Level;
|
import java.util.logging.Logger;
|
import org.farng.mp3.MP3File;
|
import org.farng.mp3.TagException;
|
import org.farng.mp3.id3.AbstractID3v2;
|
import org.farng.mp3.id3.ID3v1;
|
|
/**
|
* Die Klasse ListFileHandler gibt die Media-Inhalte eines Ablageortes
|
* als Liste aus. Audio-Inhalte werden dabei unter Verwendung von
|
* Informationen aus ID3-Tags dargestellt, z.B. Artist, Album, Titel,
|
* sofern solche vorhanden sind. Zudem werden nur diejenigen Inahlte
|
* ausgegeben, die Dateierweiterungen besitzen, wie sie in der FileStorage
|
* unter 'Einstellung' unter audioexts, videoexts und imageexts angegeben
|
* sind (die Namen der Einstellungen sind ueber das Resource Bundle von
|
* Tango ueber RB_AUDIOEXTS, RB_VIDEOEXTS und RB_FOTOEXTS veraenderbar).
|
*
|
* Ein ListFileHandler gibt dann fuer Ordner den Inhalt als Liste aus und
|
* Streamt den Inhalt von Dateien.
|
*
|
* Der ListFileHandler modelliert das Verhalten, das auf der Bedienoberflaeche
|
* von Tango in der Konfiguration als 'Kataloge' angelegt werden kann. Die
|
* HTTP Servicepunkte ergeben sich aus den Ablageort-Objekten die fuer
|
* Kataloge vom Benutzer in der Konfiguration angelegt werden.
|
*
|
* Der vom Benutzer gewaehlte URL eines Ablagortes wird vom ListFileHandler zu
|
* dem Pfad des Ablageortes hin verknuepft. So ergibt sich fuer jeden
|
* ListFileHandler der Endpunkt
|
*
|
* HTTP GET http://mein-server/tango/[Ablageort-URL]/
|
*
|
* Ist z.B. Audio und Video unter dem Pfad /media/extssd/mc abgelegt, kann
|
* ein Ablageort namens 'AV' den URL /media fuer diesen Pfad definieren. Dann verweist
|
*
|
* http://mein-server/tango/media/
|
*
|
* auf den Inhalt unter /media/extssd/mc
|
*
|
* Hierbei wird der Inhalt unter dem Katalognamen 'AV' in der Bedienoberflaeche von
|
* Tango dargestellt, d.h., die Auswahl von 'AV' an der Bedienoberflaeche bewirkt
|
* 'unter der Haube' den Abruf von http://mein-server/tango/media/
|
*
|
* Selbstverstaendlich kann aber dieser URL auch von ueberallher verwendet werden.
|
* Die Verknuepfung zwischen Katalogname und URL besteht nur an der Bedienoberflaeche
|
* von Tango.
|
*
|
* @author Ulrich Hilger
|
*/
|
public class ListFileHandler extends FileHandler {
|
|
public static final String RB_AUDIOEXTS = "audioexts";
|
public static final String RB_VIDEOEXTS = "videoexts";
|
public static final String RB_FOTOEXTS = "imageexts";
|
|
/* Der Logger fuer diesen ListFileHandler */
|
private static final Logger logger = Logger.getLogger(ListFileHandler.class.getName());
|
|
private static final String[] specialChars = {new String("\u00c4"), new String("\u00d6"),
|
new String("\u00dc"), new String("\u00e4"), new String("\u00f6"), new String("\u00fc"), new String("\u00df")};
|
|
public static final String UNWANTED_PATTERN = "[^a-zA-Z_0-9 ]";
|
|
Map extMap = new HashMap();
|
|
private String conf;
|
|
public ListFileHandler(String absoluteDirectoryPathAndName, String conf) {
|
super(absoluteDirectoryPathAndName);
|
/*
|
Ermittlung von Dateifiltern.
|
Sie werden erwartet in den Einstellungen 'audioexts' und 'videoexts'
|
jeweils als Dateierweiterungen mit Komma getrennt
|
z.B. "mp4,m4v"
|
*/
|
FileStorage fs = new FileStorage(conf);
|
initMap(fs, getResString(RB_AUDIOEXTS), StorageFile.TYP_AUDIO);
|
initMap(fs, getResString(RB_VIDEOEXTS), StorageFile.TYP_VIDEO);
|
initMap(fs, getResString(RB_FOTOEXTS), StorageFile.TYP_FOTO);
|
}
|
|
private void initMap(Storage s, String key, String typ) {
|
Entity e = s.read(Einstellung.class.getSimpleName(), key);
|
if(e instanceof Einstellung) {
|
String[] exts = ((Einstellung) e).getValue().split(",");
|
for(String ext : exts) {
|
extMap.put(ext, typ);
|
}
|
}
|
}
|
|
@Override
|
public void handle(HttpExchange e) throws IOException {
|
String path = e.getRequestURI().toString();
|
logger.fine(path);
|
if(path.endsWith(Server.SLASH)) {
|
String fName = getFileName(e);
|
logger.fine(fName);
|
File dir = new File(fileBase, fName);
|
logger.fine(dir.getAbsolutePath());
|
File[] files = dir.listFiles(new FileFilter() {
|
@Override
|
public boolean accept(File pathname) {
|
Set keys = extMap.keySet();
|
String fname = pathname.getName();
|
int pos = fname.lastIndexOf(".");
|
String ext = fname.substring(pos+1);
|
return keys.contains(ext) || pathname.isDirectory();
|
}
|
});
|
Arrays.sort(files);
|
ArrayList list = new ArrayList();
|
if(files != null) {
|
for(File file : files) {
|
StorageFile sf = new StorageFile();
|
String fname = file.getName();
|
sf.setName(fname);
|
sf.setTitelAnzName(fname);
|
if(file.isDirectory()) {
|
sf.setTyp(StorageFile.TYP_FOLDER);
|
} else {
|
int pos = fname.lastIndexOf(".");
|
String ext = fname.substring(pos+1);
|
logger.log(Level.FINE, "ext: {0}", ext);
|
Object o = extMap.get(ext);
|
if(o instanceof String) {
|
sf.setTyp(o.toString());
|
getMetadata(file, sf);
|
} else {
|
sf.setTyp(StorageFile.TYP_FILE);
|
}
|
}
|
list.add(sf);
|
}
|
}
|
//Collections.sort(list);
|
String rawjson = jsonWithCustomType(list, "Medialiste");
|
String json = escapeHtml(rawjson);
|
|
logger.fine(json);
|
Headers headers = e.getResponseHeaders();
|
headers.add("Content-Type", "application/json; charset=UTF-8");
|
e.sendResponseHeaders(200, json.length());
|
OutputStream os = e.getResponseBody();
|
os.write(json.getBytes());
|
os.close();
|
} else {
|
super.handle(e);
|
}
|
}
|
|
public String escapeHtml(String text) {
|
text = text.replace(specialChars[0], "Ae");
|
text = text.replace(specialChars[1], "Oe");
|
text = text.replace(specialChars[2], "Ue");
|
text = text.replace(specialChars[3], "ae");
|
text = text.replace(specialChars[4], "oe");
|
text = text.replace(specialChars[5], "ue");
|
text = text.replace(specialChars[6], "ss");
|
|
/*
|
text = text.replace(specialChars[0], "Ä");
|
text = text.replace(specialChars[1], "Ö");
|
text = text.replace(specialChars[2], "Ü");
|
text = text.replace(specialChars[3], "ä");
|
text = text.replace(specialChars[4], "ö");
|
text = text.replace(specialChars[5], "ü");
|
text = text.replace(specialChars[6], "ß");
|
*/
|
return text;
|
}
|
|
private void getMetadata(File file, StorageFile sf) {
|
if(sf.getTyp().equalsIgnoreCase(StorageFile.TYP_AUDIO)) {
|
try {
|
MP3File mp3 = new MP3File(file);
|
ID3v1 tag = mp3.getID3v1Tag();
|
if(tag == null) {
|
AbstractID3v2 tag2 = mp3.getID3v2Tag();
|
sf.setInterpret(tag2.getLeadArtist().replaceAll(UNWANTED_PATTERN, ""));
|
String trackTitel = tag2.getSongTitle().replaceAll(UNWANTED_PATTERN, "");
|
if(trackTitel != null && trackTitel.length() > 0) {
|
sf.setTitelAnzName(trackTitel);
|
}
|
sf.setAlbum(tag2.getAlbumTitle().replaceAll(UNWANTED_PATTERN, ""));
|
} else {
|
sf.setInterpret(tag.getArtist().replaceAll(UNWANTED_PATTERN, ""));
|
String trackTitel = tag.getTitle().replaceAll(UNWANTED_PATTERN, "");
|
if(trackTitel != null && trackTitel.length() > 0) {
|
sf.setTitelAnzName(trackTitel);
|
}
|
sf.setAlbum(tag.getAlbumTitle().replaceAll(UNWANTED_PATTERN, ""));
|
}
|
} catch (IOException ex) {
|
Logger.getLogger(ListFileHandler.class.getName()).log(Level.SEVERE, null, ex);
|
} catch (TagException ex) {
|
Logger.getLogger(ListFileHandler.class.getName()).log(Level.SEVERE, null, ex);
|
}
|
}
|
}
|
|
}
|