|
@@ -0,0 +1,338 @@
|
|
|
+/*
|
|
|
+ * Copyright (C) 2015 iZc
|
|
|
+ *
|
|
|
+ * This program is free software: you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU 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 General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+ */
|
|
|
+
|
|
|
+package de.nplusc.izc.iZpl.Utils.shared;
|
|
|
+
|
|
|
+import de.nplusc.izc.iZpl.API.shared.InvalidPlayListFileException;
|
|
|
+import de.nplusc.izc.iZpl.API.shared.MultiPlayListItem;
|
|
|
+import de.nplusc.izc.iZpl.API.shared.PlayListFile;
|
|
|
+import de.nplusc.izc.iZpl.API.shared.PlayListItem;
|
|
|
+import de.nplusc.izc.iZpl.API.shared.SinglePlayListItem;
|
|
|
+//import de.nplusc.izc.iZpl.Main;
|
|
|
+import java.io.BufferedReader;
|
|
|
+import java.io.File;
|
|
|
+import java.io.FileReader;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.RandomAccessFile;
|
|
|
+import java.nio.file.Path;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import org.apache.logging.log4j.LogManager;
|
|
|
+import org.apache.logging.log4j.Logger;
|
|
|
+import org.yaml.snakeyaml.Yaml;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author iZc <nplusc.de>
|
|
|
+ */
|
|
|
+public class PLFileIO
|
|
|
+{
|
|
|
+ //Formatdefinition //basiert aufne verpackte #extm3u
|
|
|
+ /*
|
|
|
+ * #EXTM3U
|
|
|
+ * #iZPl
|
|
|
+ * #EXTINFO.......//Die VLC-Daten
|
|
|
+ * #IZPL:PrioMultiplier|GroupID (IDS !=0 sind Verbunden. 0er immer einzeln)
|
|
|
+ * /Pfad/zu/File1.mp3
|
|
|
+ * //Defaultet zu 1,0 falls nicht gefunden
|
|
|
+ * #IZPL:INCLUDE|PrioMultiplier|Expand//INCLUDE sagt dass hier ne referenzierte IZPL liegt Expand sagt Liste kann aufgeteilt werden
|
|
|
+ * /Pfad/zu/andererListe.iZpl //prioMultiplier bei include mutipliziert Multiopliers des Includes
|
|
|
+ * #IZPL:INCLUDE|PrioMultiplier|NoExpand //NoExpand sagt dass File Als ein Block behandelt werden muss (Includes innerhalb werden auch beachtet aber keine multiplizierungen eingebaut
|
|
|
+ * /Pfad/zu/UnsplittbarerListe.iZpl
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+ private static Yaml y = new Yaml();
|
|
|
+ private static final Logger l = LogManager.getLogger();
|
|
|
+ public static List<SinglePlayListItem> readSingleList(String path) throws InvalidPlayListFileException
|
|
|
+ {
|
|
|
+ String plbd = new File(path).getParent()+File.separator;
|
|
|
+ l.trace("Location:{}",new File(path).getAbsolutePath());
|
|
|
+ try
|
|
|
+ {
|
|
|
+ boolean syntaxError=false;
|
|
|
+ BufferedReader fr = new BufferedReader(new FileReader(path));
|
|
|
+
|
|
|
+ if(!(fr.readLine().equalsIgnoreCase("#EXTM3U")))
|
|
|
+ {
|
|
|
+ l.error("Not a valid izpl or M3u-file");
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ if(!fr.readLine().startsWith("#IZPL"))
|
|
|
+ {
|
|
|
+ l.info("plain M3u detected");
|
|
|
+ }
|
|
|
+ int lne=3;
|
|
|
+ ArrayList<SinglePlayListItem> returndata = new ArrayList<>();
|
|
|
+ String ln = fr.readLine();
|
|
|
+ boolean extinf=false;
|
|
|
+ SinglePlayListItem itm = new SinglePlayListItem();
|
|
|
+ while(ln!=null)
|
|
|
+ {
|
|
|
+ if(!ln.trim().equals(""))
|
|
|
+ {
|
|
|
+ l.trace(ln);
|
|
|
+ if(ln.startsWith("#IZPL"))
|
|
|
+ {
|
|
|
+ String[] meta = ln.split(":")[1].split("\\|");
|
|
|
+ if(meta[0].equalsIgnoreCase("include"))
|
|
|
+ {
|
|
|
+ if(meta.length==3)
|
|
|
+ {
|
|
|
+ if(meta[2].equalsIgnoreCase("expand"))
|
|
|
+ {
|
|
|
+ itm.setIncludeElements(true, false);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(meta[2].equalsIgnoreCase("noexpand"))
|
|
|
+ {
|
|
|
+ itm.setIncludeElements(true, true);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ syntaxError=true;
|
|
|
+ l.error("Syntax error on line {} of file {}: {}",lne,path,"Include-Block haben nur Expand oder NoExpand als dritten block");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ try
|
|
|
+ {
|
|
|
+ int pr = Integer.valueOf(meta[1]);
|
|
|
+ if(pr<1)
|
|
|
+ {
|
|
|
+ throw new NumberFormatException();
|
|
|
+ }
|
|
|
+ itm.setTargetPlaycount(pr);
|
|
|
+ }
|
|
|
+ catch(NumberFormatException x)
|
|
|
+ {
|
|
|
+ syntaxError=true;
|
|
|
+ l.error("Syntax error on line {} of file {}: {}",lne,path,"der 2. block bei Include muss eine positive Ganzzahl sein");
|
|
|
+ itm.setTargetPlaycount(1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ syntaxError=true;
|
|
|
+ l.error("Syntax error on line {} of file {}: {}",lne,path,"Include-Block braucht 2 weitere blöcke um korrekt zu sein");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(meta.length==2)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ int pr = Integer.valueOf(meta[0]);
|
|
|
+ if (pr < 1)
|
|
|
+ {
|
|
|
+ throw new NumberFormatException();
|
|
|
+ }
|
|
|
+ itm.setTargetPlaycount(pr);
|
|
|
+ }
|
|
|
+ catch (NumberFormatException x)
|
|
|
+ {
|
|
|
+ syntaxError = true;
|
|
|
+ l.error("Syntax error on line {} of file {}: {}", lne, path, "Block1 muss eine Ganzzahl > 0 sein");
|
|
|
+ itm.setTargetPlaycount(1);
|
|
|
+ }
|
|
|
+ try
|
|
|
+ {
|
|
|
+ l.trace(meta[1]);
|
|
|
+ int gid = Integer.valueOf(meta[1].trim());
|
|
|
+ if (gid < 0)
|
|
|
+ {
|
|
|
+ l.trace("NULL0");
|
|
|
+ throw new NumberFormatException();
|
|
|
+ }
|
|
|
+ itm.setGID(gid);
|
|
|
+ }
|
|
|
+ catch (NumberFormatException x)
|
|
|
+ {
|
|
|
+ syntaxError = true;
|
|
|
+ l.error("Syntax error on line {} of file {}: {}", lne, path, "Block2 muss eine Ganzzahl >=0 sein");
|
|
|
+ itm.setGID(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ syntaxError=true;
|
|
|
+ l.error("Syntax error on line {} of file {}: {}",lne,path,"Could not determine Metadata block type");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(ln.startsWith("#EXTINF"))
|
|
|
+ {
|
|
|
+ extinf=true;
|
|
|
+ itm.setTitle(ln);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(!ln.startsWith("#"))
|
|
|
+ {
|
|
|
+ if(!extinf)
|
|
|
+ {
|
|
|
+ itm.setTitle("#EXTINF,0,"+new File(ln).getName());
|
|
|
+ }
|
|
|
+ if(!(ln.substring(1).startsWith(":")||ln.startsWith(File.separator)))
|
|
|
+ {
|
|
|
+ ln=plbd+ln;//relative pfade absolutisieren
|
|
|
+ }
|
|
|
+ itm.setPath(ln);
|
|
|
+ returndata.add(itm);
|
|
|
+ itm=new SinglePlayListItem();
|
|
|
+ extinf=false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lne++;
|
|
|
+ ln=fr.readLine();
|
|
|
+ }
|
|
|
+ if(syntaxError)
|
|
|
+ {
|
|
|
+ throw new InvalidPlayListFileException();
|
|
|
+ }
|
|
|
+ return returndata;
|
|
|
+ }
|
|
|
+ catch (IOException ex)
|
|
|
+ {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static List<PlayListItem> parseFullList(String path) throws InvalidPlayListFileException
|
|
|
+ {
|
|
|
+ return parseFullList(path, 40);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static List<PlayListItem> parseFullList(String path,int rd) throws InvalidPlayListFileException
|
|
|
+ {
|
|
|
+
|
|
|
+ HashMap<Integer,MultiPlayListItem> groups = new HashMap<>();
|
|
|
+ List<PlayListItem> result = new ArrayList<>();
|
|
|
+
|
|
|
+ List<SinglePlayListItem> rootList = readSingleList(path);
|
|
|
+ l.trace(y.dump(rootList));
|
|
|
+ for (SinglePlayListItem re : rootList)
|
|
|
+ {
|
|
|
+ if(re.isIncludeElement())
|
|
|
+ {
|
|
|
+ if(re.noexpandoninclude())
|
|
|
+ {
|
|
|
+ MultiPlayListItem m = processNoExpandInclude(re.getPath(),rd);
|
|
|
+ if(m!=null)
|
|
|
+ {
|
|
|
+ m.setTargetPlaycount(re.getTargetPlaycount());
|
|
|
+ result.add(m);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ List<PlayListItem> temp = parseFullList(re.getPath(),rd-1);
|
|
|
+ temp.forEach((ple)->ple.setTargetPlaycount(ple.getTargetPlaycount()*re.getTargetPlaycount()));
|
|
|
+ result.addAll(temp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ int gid=re.getGID();
|
|
|
+ if(gid==0)
|
|
|
+ {
|
|
|
+ result.add(re);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(!groups.containsKey(gid))
|
|
|
+ {
|
|
|
+ groups.put(gid,new MultiPlayListItem());
|
|
|
+ }
|
|
|
+ MultiPlayListItem gt = groups.get(gid);
|
|
|
+ gt.addItem(re);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static MultiPlayListItem processNoExpandInclude(String path,int rd) throws InvalidPlayListFileException
|
|
|
+ {
|
|
|
+ MultiPlayListItem mi = new MultiPlayListItem();
|
|
|
+ if(rd<0)
|
|
|
+ {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ rd--;
|
|
|
+ List<SinglePlayListItem> raw = readSingleList(path);
|
|
|
+ for (SinglePlayListItem spi : raw)
|
|
|
+ {
|
|
|
+ if(spi.isIncludeElement())
|
|
|
+ {
|
|
|
+ mi.addItem(processNoExpandInclude(spi.getPath(), rd));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ mi.addItem(spi);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return mi;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void writePLFile(PlayListFile f)
|
|
|
+ {
|
|
|
+ Path p = new File(f.getPath()).getParentFile().toPath();
|
|
|
+ try
|
|
|
+ {
|
|
|
+ String fp = f.getPath();
|
|
|
+ String pld = "#EXTM3U\n#IZPL\n";
|
|
|
+ for (SinglePlayListItem pli : f.getEntries())
|
|
|
+ {
|
|
|
+ String pathOfFile = p.relativize(new File(pli.getPath()).toPath()).toString();
|
|
|
+
|
|
|
+
|
|
|
+ if(pli.isIncludeElement())
|
|
|
+ {
|
|
|
+ pld+="#IZPL:INCLUDE|"+pli.getTargetPlaycount()+"|"+(pli.noexpandoninclude()?"NOEXPAND":"EXPAND")+"\n"+pathOfFile+"\n";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ pld+=pli.getTitle()+"\n"+"#IZPL:"+pli.getTargetPlaycount()+"|"+pli.getGID()+"\n"+pathOfFile+"\n";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ RandomAccessFile file = new RandomAccessFile(fp, "rw");
|
|
|
+ file.setLength(0);
|
|
|
+ file.write(pld.getBytes());
|
|
|
+ }
|
|
|
+ catch (IOException ex)
|
|
|
+ {
|
|
|
+ ex.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private PLFileIO()
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|