/**
* Copyright (c) 2015 unfoldingWord
* http://creativecommons.org/licenses/MIT/
* See LICENSE file for details.
* Contributors:
* PJ Fechner <pj@actsmedia.com>
*/
package model.parsers;
import android.net.Uri;
import org.spongycastle.util.Arrays;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import model.AudioMarker;
import utils.FileUtil;
/**
* Created by Fechner on 10/2/15.
*/
public class AudioMarkerParser {
private static final String TAG = "AudioMarkerParser";
private static final Pattern TRACKS_REGEX = Pattern.compile("<xmpDM:Tracks>(.|\\n)*</xmpDM:Tracks>");
// private static final String TRACKS
public static List<AudioMarker> createAudioMarkers(Uri audioUri, long trackLength){
Element description = getDescriptionElement(getNeededText(audioUri));
long frameRate = getFrameRate(description);
List<AudioMarker> markers = getAudioMarkersFromDescription(description, frameRate);
Collections.sort(markers);
markers = AudioMarker.createLengths(markers, trackLength);
return markers;
}
private static String getNeededText(Uri audioUri){
File audioFile = new File(audioUri.getPath());
byte[] headerBytes = getHeaderBytes(audioFile);
if(headerBytes == null){
return null;
}
String headerText = new String(headerBytes);
return getTracksText(headerText);
}
private static byte[] getHeaderBytes(File file){
byte[] audioBytes = FileUtil.getBytesFromFile(file);
if(audioBytes == null){
return null;
}
return Arrays.copyOfRange(audioBytes, 0, 500 * 1000);
}
private static String getTracksText(String header){
Matcher numberMatcher = TRACKS_REGEX.matcher(header);
if(numberMatcher.find()) {
return numberMatcher.group(0);
}
else{
return null;
}
}
private static long getFrameRate(Element description){
NamedNodeMap nodes = description.getAttributes();
String frameRateValue = nodes.getNamedItem("xmpDM:frameRate").getTextContent();
frameRateValue = frameRateValue.replace("f", "");
long frameRate = Long.parseLong(frameRateValue);
return frameRate;
}
private static List<AudioMarker> getAudioMarkersFromDescription(Element descriptionElement, long frameRate){
Element markers = XMLParser.getElement(descriptionElement.getChildNodes(), "xmpDM:markers");
Element seq = XMLParser.getElement(markers.getChildNodes(), "rdf:Seq");
List<AudioMarker> markerList = new ArrayList<>();
NodeList nodes = seq.getElementsByTagName("rdf:li");
for (int i = 0; i < nodes.getLength(); i++) {
if (nodes.item(i) instanceof Element) {
Element element = (Element) nodes.item(i);
String tagName = element.getTagName();
if(tagName.equalsIgnoreCase("rdf:li")){
AudioMarker marker = getMarkFromLi(element, frameRate);
if(marker != null) {
markerList.add(marker);
}
}
}
}
return markerList;
}
private static AudioMarker getMarkFromLi(Element liElement, long frameRate){
Element description = XMLParser.getElement(liElement.getChildNodes(), "rdf:Description");
if(description != null) {
NamedNodeMap nodes = description.getAttributes();
String startTimeText = nodes.getNamedItem("xmpDM:startTime").getTextContent();
long startTime = 0;
if(startTimeText.contains("f")){
String[] splitTimeText= startTimeText.split("f");
startTime = Long.parseLong(splitTimeText[0]);
frameRate = Long.parseLong(splitTimeText[1]);
}
else{
startTime = Long.parseLong(startTimeText);
}
long duration = -1;
Node durationNode = nodes.getNamedItem("xmpDM:duration");
if (durationNode != null) {
duration = Long.parseLong(durationNode.getTextContent());
}
return new AudioMarker(getTimeForMarker(startTime, frameRate), getTimeForMarker(duration, frameRate));
}
else{
return null;
}
}
private static Element getDescriptionElement(String text){
Document doc = XMLParser.getDomElement(text);
if(doc == null){
return null;
}
NodeList nodes = doc.getElementsByTagName("xmpDM:Tracks");
Element tracks = XMLParser.getElement(nodes, "xmpDM:Tracks");
Element bag = XMLParser.getElement(tracks.getChildNodes(), "rdf:Bag");
Element li = XMLParser.getElement(bag.getChildNodes(), "rdf:li");
return XMLParser.getElement(li.getChildNodes(), "rdf:Description");
}
private static long getTimeForMarker(long markerValue, long frameRate){
return (markerValue / frameRate) * 1000;
}
}