/* 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 . */ package de.uhilger.tango.api; import com.google.gson.Gson; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import de.uhilger.tango.Server; import de.uhilger.tango.entity.Abspielliste; import de.uhilger.tango.entity.Entity; import de.uhilger.tango.entity.Titel; import de.uhilger.tango.store.FileStorage; import java.io.IOException; import java.util.List; import java.util.logging.Logger; /** * Der ListHandler bearbeitet HTTP-Anfragen zu Abspiellisten * * GET /tango/api/alist/[pl-name] die Titel-Objekte der Liste [pl-name] liefern * GET /tango/api/alist/[pl-name]/m3u eine einfache Playlist im M3U-Format ausgeben * GET /tango/api/alist/[pl-name]/[nr] den Titel mit der Nummer [nr] abrufen * * PUT /tango/api/alist/[pl-name] den Titel im Body anfuegen an die Liste [pl-name] * PUT /tango/api/alist/[pl-name]/[nr] an der Position nr der Liste [pl-name] den Titel im Body einfuegen * PUT /tango/api/alist/[pl-name]/[nrVon]/[nrNach] den Titel von seiner aktuellen Position an eine * andere Position der Liste [pl-name] verschieben * DELETE /tango/api/alist/[pl-name]/[nr] den Titel an der Position [nr] aus der Liste [pl-name] entfernen * DELETE /tango/api/alist/[pl-name]/alle alle Titel aus der Liste [pl-name] entfernen * * TODO (2.1.2023): * - Liste ab Titel spielen * - Ganzes Album der Liste hinzufuegen * * @author Ulrich Hilger * @version 1, 8.4.2021 */ public class ListHandler extends AbstractHandler { private static final Logger logger = Logger.getLogger(ListHandler.class.getName()); public static final String ALLE_TITEL = "alle"; private String conf; public ListHandler(String conf) { this.conf = conf; } @Override protected String get(HttpExchange e) { String path = e.getRequestURI().toString(); String[] elems = path.split(Server.SLASH); if(elems.length > 5) { if(elems[5].endsWith("m3u")) { Headers headers = e.getResponseHeaders(); headers.add("Content-Type", "application/m3u"); return getM3u(e, elems[4]); } else { try { int index = Integer.parseInt(elems[5]); return getTitel(elems[4], index); } catch(NumberFormatException ex) { return "ungueltig"; } } } else { String plname = elems[elems.length - 1]; FileStorage fs = new FileStorage(conf); String json = fs.readJson(FileStorage.ST_ABSPIELLISTE, plname); return embedInCustomType(json, FileStorage.ST_ABSPIELLISTE); } } private String getM3u(HttpExchange e, String plname) { StringBuilder sb = new StringBuilder(); FileStorage fs = new FileStorage(conf); Entity entity = fs.read(FileStorage.ST_ABSPIELLISTE, plname); if (entity instanceof Abspielliste) { Abspielliste liste = (Abspielliste) entity; List titelListe = liste.getTitel(); for(Titel titel : titelListe) { String server = getEinstellung(fs, getResString(MediaSteuerung.RB_HOST), MediaSteuerung.DEFAULT_HOST); sb.append(server); sb.append(titel.getKatalogUrl()); sb.append(titel.getPfad()); sb.append(titel.getName()); sb.append(Server.NEWLINE); } } return sb.toString(); } @Override protected String put(HttpExchange e) throws IOException { String path = e.getRequestURI().toString(); String[] elems = path.split(Server.SLASH); String response = "ListHandler.put: ungueltiger URL"; switch(elems.length) { case 5: // ohne nr am Ende response = addTitel(e, elems[4]); break; case 6: response = insertTitel(e, elems[4], Integer.parseInt(elems[5])); break; case 7: response = moveTitel(e, elems[4], Integer.parseInt(elems[5]), Integer.parseInt(elems[6])); break; } return response; } private String getTitel(String plname, int index) { FileStorage fs = new FileStorage(conf); Entity entity = fs.read(FileStorage.ST_ABSPIELLISTE, plname); String response = "eom"; if(entity instanceof Abspielliste) { Abspielliste aliste = (Abspielliste) entity; //String titelJson = bodyLesen(e); List titelListe = aliste.getTitel(); if(index < titelListe.size()) { Titel titel = aliste.getTitel().get(index); Gson gson = new Gson(); response = gson.toJson(titel); } } return response; } /** * Den Titel im Body von seiner aktuellen Position an die angegebene * Position setzen. Der Titel an der angegebenen Position rueckt nach * unten. * * Annahme: Die Abspielliste enthaelt keine Titel mehrfach. * * @param e * @param plname * @param zielPos * @return * @throws IOException */ private String moveTitel(HttpExchange e, String plname, int pos, int zielPos) throws IOException { FileStorage fs = new FileStorage(conf); Entity entity = fs.read(FileStorage.ST_ABSPIELLISTE, plname); String response = "Titel konnte nicht verschoben werden."; if(entity instanceof Abspielliste) { if(pos < zielPos) { --zielPos; } Abspielliste aliste = (Abspielliste) entity; List liste = aliste.getTitel(); Titel t = liste.get(pos); liste.remove(pos); liste.add(zielPos, t); fs.write(aliste, true); response = "Titel " + t.getName() + " der Liste " + aliste.getName() + " an Position " + zielPos + " verschoben."; } return response; } /** * Den Titel im Body an Position anPos einfuegen. Der Titel an anPos * rueckt nach unten. * * @param e * @param plname * @param anPos * @return * @throws IOException */ private String insertTitel(HttpExchange e, String plname, int anPos) throws IOException { FileStorage fs = new FileStorage(conf); Entity entity = fs.read(FileStorage.ST_ABSPIELLISTE, plname); String response = "Titel konnte nicht hinzugefuegt werden."; if(entity instanceof Abspielliste) { Abspielliste aliste = (Abspielliste) entity; String titelJson = bodyLesen(e); Gson gson = new Gson(); Object o = gson.fromJson(titelJson, fs.typeFromName(Titel.class.getSimpleName()).getType()); if(o instanceof Titel) { Titel titel = (Titel) o; aliste.putTitel(titel, anPos); fs.write(aliste, true); response = "Titel " + titel.getName() + " der Liste " + aliste.getName() + " an Position " + anPos + " hinzugefuegt."; } } return response; } private String addTitel(HttpExchange e, String plname) throws IOException { FileStorage fs = new FileStorage(conf); Entity entity = fs.read(FileStorage.ST_ABSPIELLISTE, plname); String response = "Titel konnte nicht hinzugefuegt werden."; if(entity instanceof Abspielliste) { Abspielliste aliste = (Abspielliste) entity; String titelJson = bodyLesen(e); Gson gson = new Gson(); Object o = gson.fromJson(titelJson, fs.typeFromName(Titel.class.getSimpleName()).getType()); if(o instanceof Titel) { Titel titel = (Titel) o; aliste.addTitel(titel); fs.write(aliste, true); response = "Titel " + titel.getName() + " der Liste " + aliste.getName() + " hinzugefuegt."; } } return response; } // DELETE /mz/api/alist/[pl-name]/[nr] den Titel an der Position [nr] aus der Liste [pl-name] entfernen @Override protected boolean delete(HttpExchange e) { String path = e.getRequestURI().toString(); String[] elems = path.split(Server.SLASH); String listName = elems[elems.length - 2]; FileStorage fs = new FileStorage(conf); Entity entity = fs.read(Abspielliste.class.getSimpleName(), listName); if(entity instanceof Abspielliste) { Abspielliste liste = (Abspielliste) entity; String titelStr = elems[elems.length-1]; if(titelStr.equalsIgnoreCase(ALLE_TITEL)) { liste.getTitel().clear(); } else { liste.getTitel().remove(Integer.parseInt(elems[elems.length-1])); } fs.write(liste, true); return true; } else { return false; } } }