Reformatted vorbis reader for readability

This commit is contained in:
ByteHamster 2020-02-12 23:26:30 +01:00
parent 9db5450243
commit 246a2e650c
2 changed files with 221 additions and 242 deletions

View File

@ -10,93 +10,91 @@ import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.VorbisCommentChapter; import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
public class VorbisCommentChapterReader extends VorbisCommentReader { public class VorbisCommentChapterReader extends VorbisCommentReader {
private static final String TAG = "VorbisCommentChptrReadr"; private static final String TAG = "VorbisCommentChptrReadr";
private static final String CHAPTER_KEY = "chapter\\d\\d\\d.*"; private static final String CHAPTER_KEY = "chapter\\d\\d\\d.*";
private static final String CHAPTER_ATTRIBUTE_TITLE = "name"; private static final String CHAPTER_ATTRIBUTE_TITLE = "name";
private static final String CHAPTER_ATTRIBUTE_LINK = "url"; private static final String CHAPTER_ATTRIBUTE_LINK = "url";
private List<Chapter> chapters; private List<Chapter> chapters;
public VorbisCommentChapterReader() { public VorbisCommentChapterReader() {
} }
@Override @Override
public void onVorbisCommentFound() { public void onVorbisCommentFound() {
System.out.println("Vorbis comment found"); System.out.println("Vorbis comment found");
} }
@Override @Override
public void onVorbisCommentHeaderFound(VorbisCommentHeader header) { public void onVorbisCommentHeaderFound(VorbisCommentHeader header) {
chapters = new ArrayList<>(); chapters = new ArrayList<>();
System.out.println(header.toString()); System.out.println(header.toString());
} }
@Override @Override
public boolean onContentVectorKey(String content) { public boolean onContentVectorKey(String content) {
return content.matches(CHAPTER_KEY); return content.matches(CHAPTER_KEY);
} }
@Override @Override
public void onContentVectorValue(String key, String value) public void onContentVectorValue(String key, String value) throws VorbisCommentReaderException {
throws VorbisCommentReaderException { if (BuildConfig.DEBUG) {
if (BuildConfig.DEBUG) Log.d(TAG, "Key: " + key + ", value: " + value);
Log.d(TAG, "Key: " + key + ", value: " + value); }
String attribute = VorbisCommentChapter.getAttributeTypeFromKey(key); String attribute = VorbisCommentChapter.getAttributeTypeFromKey(key);
int id = VorbisCommentChapter.getIDFromKey(key); int id = VorbisCommentChapter.getIDFromKey(key);
Chapter chapter = getChapterById(id); Chapter chapter = getChapterById(id);
if (attribute == null) { if (attribute == null) {
if (getChapterById(id) == null) { if (getChapterById(id) == null) {
// new chapter // new chapter
long start = VorbisCommentChapter.getStartTimeFromValue(value); long start = VorbisCommentChapter.getStartTimeFromValue(value);
chapter = new VorbisCommentChapter(id); chapter = new VorbisCommentChapter(id);
chapter.setStart(start); chapter.setStart(start);
chapters.add(chapter); chapters.add(chapter);
} else { } else {
throw new VorbisCommentReaderException( throw new VorbisCommentReaderException("Found chapter with duplicate ID (" + key + ", " + value + ")");
"Found chapter with duplicate ID (" + key + ", " }
+ value + ")"); } else if (attribute.equals(CHAPTER_ATTRIBUTE_TITLE)) {
} if (chapter != null) {
} else if (attribute.equals(CHAPTER_ATTRIBUTE_TITLE)) { chapter.setTitle(value);
if (chapter != null) { }
chapter.setTitle(value); } else if (attribute.equals(CHAPTER_ATTRIBUTE_LINK)) {
} if (chapter != null) {
} else if (attribute.equals(CHAPTER_ATTRIBUTE_LINK)) { chapter.setLink(value);
if (chapter != null) { }
chapter.setLink(value); }
} }
}
}
@Override @Override
public void onNoVorbisCommentFound() { public void onNoVorbisCommentFound() {
System.out.println("No vorbis comment found"); System.out.println("No vorbis comment found");
} }
@Override @Override
public void onEndOfComment() { public void onEndOfComment() {
System.out.println("End of comment"); System.out.println("End of comment");
for (Chapter c : chapters) { for (Chapter c : chapters) {
System.out.println(c.toString()); System.out.println(c.toString());
} }
} }
@Override @Override
public void onError(VorbisCommentReaderException exception) { public void onError(VorbisCommentReaderException exception) {
exception.printStackTrace(); exception.printStackTrace();
} }
private Chapter getChapterById(long id) { private Chapter getChapterById(long id) {
for (Chapter c : chapters) { for (Chapter c : chapters) {
if (((VorbisCommentChapter) c).getVorbisCommentId() == id) { if (((VorbisCommentChapter) c).getVorbisCommentId() == id) {
return c; return c;
} }
} }
return null; return null;
} }
public List<Chapter> getChapters() { public List<Chapter> getChapters() {
return chapters; return chapters;
} }
} }

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.core.util.vorbiscommentreader; package de.danoeh.antennapod.core.util.vorbiscommentreader;
import androidx.annotation.NonNull;
import org.apache.commons.io.EndianUtils; import org.apache.commons.io.EndianUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
@ -10,185 +11,165 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Arrays; import java.util.Arrays;
public abstract class VorbisCommentReader { public abstract class VorbisCommentReader {
/** Length of first page in an ogg file in bytes. */ /** Length of first page in an ogg file in bytes. */
private static final int FIRST_PAGE_LENGTH = 58; private static final int FIRST_PAGE_LENGTH = 58;
private static final int SECOND_PAGE_MAX_LENGTH = 64 * 1024 * 1024; private static final int SECOND_PAGE_MAX_LENGTH = 64 * 1024 * 1024;
private static final int PACKET_TYPE_IDENTIFICATION = 1; private static final int PACKET_TYPE_IDENTIFICATION = 1;
private static final int PACKET_TYPE_COMMENT = 3; private static final int PACKET_TYPE_COMMENT = 3;
/** Called when Reader finds identification header. */ /** Called when Reader finds identification header. */
protected abstract void onVorbisCommentFound(); protected abstract void onVorbisCommentFound();
protected abstract void onVorbisCommentHeaderFound(VorbisCommentHeader header); protected abstract void onVorbisCommentHeaderFound(VorbisCommentHeader header);
/** /**
* Is called every time the Reader finds a content vector. The handler * Is called every time the Reader finds a content vector. The handler
* should return true if it wants to handle the content vector. * should return true if it wants to handle the content vector.
*/ */
protected abstract boolean onContentVectorKey(String content); protected abstract boolean onContentVectorKey(String content);
/** /**
* Is called if onContentVectorKey returned true for the key. * Is called if onContentVectorKey returned true for the key.
* */
* @throws VorbisCommentReaderException protected abstract void onContentVectorValue(String key, String value) throws VorbisCommentReaderException;
*/
protected abstract void onContentVectorValue(String key, String value)
throws VorbisCommentReaderException;
protected abstract void onNoVorbisCommentFound(); protected abstract void onNoVorbisCommentFound();
protected abstract void onEndOfComment(); protected abstract void onEndOfComment();
protected abstract void onError(VorbisCommentReaderException exception); protected abstract void onError(VorbisCommentReaderException exception);
public void readInputStream(InputStream input) public void readInputStream(InputStream input) throws VorbisCommentReaderException {
throws VorbisCommentReaderException { try {
try { // look for identification header
// look for identification header if (findIdentificationHeader(input)) {
if (findIdentificationHeader(input)) { onVorbisCommentFound();
input = new OggInputStream(input);
if (findCommentHeader(input)) {
VorbisCommentHeader commentHeader = readCommentHeader(input);
onVorbisCommentHeaderFound(commentHeader);
for (int i = 0; i < commentHeader.getUserCommentLength(); i++) {
readUserComment(input);
}
onEndOfComment();
} else {
onError(new VorbisCommentReaderException("No comment header found"));
}
} else {
onNoVorbisCommentFound();
}
} catch (IOException e) {
onError(new VorbisCommentReaderException(e));
}
}
onVorbisCommentFound(); private void readUserComment(InputStream input) throws VorbisCommentReaderException {
input = new OggInputStream(input); try {
if (findCommentHeader(input)) { long vectorLength = EndianUtils.readSwappedUnsignedInteger(input);
VorbisCommentHeader commentHeader = readCommentHeader(input); String key = readContentVectorKey(input, vectorLength).toLowerCase();
if (commentHeader != null) { boolean readValue = onContentVectorKey(key);
onVorbisCommentHeaderFound(commentHeader); if (readValue) {
for (int i = 0; i < commentHeader String value = readUtf8String(input, (int) (vectorLength - key.length() - 1));
.getUserCommentLength(); i++) { onContentVectorValue(key, value);
try { } else {
long vectorLength = EndianUtils IOUtils.skipFully(input, vectorLength - key.length() - 1);
.readSwappedUnsignedInteger(input); }
String key = readContentVectorKey(input, } catch (IOException e) {
vectorLength).toLowerCase(); e.printStackTrace();
boolean readValue = onContentVectorKey(key); }
if (readValue) { }
String value = readUTF8String(
input,
(int) (vectorLength - key.length() - 1));
onContentVectorValue(key, value);
} else {
IOUtils.skipFully(input,
vectorLength - key.length() - 1);
}
} catch (IOException e) {
e.printStackTrace();
}
}
onEndOfComment();
}
} else { private String readUtf8String(InputStream input, long length) throws IOException {
onError(new VorbisCommentReaderException( byte[] buffer = new byte[(int) length];
"No comment header found")); IOUtils.readFully(input, buffer);
} Charset charset = Charset.forName("UTF-8");
} else { return charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString();
onNoVorbisCommentFound(); }
}
} catch (IOException e) {
onError(new VorbisCommentReaderException(e));
}
}
private String readUTF8String(InputStream input, long length) /**
throws IOException { * Looks for an identification header in the first page of the file. If an
byte[] buffer = new byte[(int) length]; * identification header is found, it will be skipped completely and the
* method will return true, otherwise false.
*/
private boolean findIdentificationHeader(InputStream input) throws IOException {
byte[] buffer = new byte[FIRST_PAGE_LENGTH];
IOUtils.readFully(input, buffer);
for (int i = 6; i < buffer.length; i++) {
if (buffer[i - 5] == 'v' && buffer[i - 4] == 'o'
&& buffer[i - 3] == 'r' && buffer[i - 2] == 'b'
&& buffer[i - 1] == 'i' && buffer[i] == 's'
&& buffer[i - 6] == PACKET_TYPE_IDENTIFICATION) {
return true;
}
}
return false;
}
IOUtils.readFully(input, buffer); private boolean findCommentHeader(InputStream input) throws IOException {
Charset charset = Charset.forName("UTF-8"); char[] buffer = new char["vorbis".length() + 1];
return charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString(); for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) {
} char c = (char) input.read();
int dest = -1;
switch (c) {
case PACKET_TYPE_COMMENT:
dest = 0;
break;
case 'v':
dest = 1;
break;
case 'o':
dest = 2;
break;
case 'r':
dest = 3;
break;
case 'b':
dest = 4;
break;
case 'i':
dest = 5;
break;
case 's':
dest = 6;
break;
}
if (dest >= 0) {
buffer[dest] = c;
if (buffer[1] == 'v' && buffer[2] == 'o' && buffer[3] == 'r'
&& buffer[4] == 'b' && buffer[5] == 'i'
&& buffer[6] == 's' && buffer[0] == PACKET_TYPE_COMMENT) {
return true;
}
} else {
Arrays.fill(buffer, (char) 0);
}
}
return false;
}
/** @NonNull
* Looks for an identification header in the first page of the file. If an private VorbisCommentHeader readCommentHeader(InputStream input) throws IOException, VorbisCommentReaderException {
* identification header is found, it will be skipped completely and the try {
* method will return true, otherwise false. long vendorLength = EndianUtils.readSwappedUnsignedInteger(input);
* String vendorName = readUtf8String(input, vendorLength);
* @throws IOException long userCommentLength = EndianUtils.readSwappedUnsignedInteger(input);
*/ return new VorbisCommentHeader(vendorName, userCommentLength);
private boolean findIdentificationHeader(InputStream input) } catch (UnsupportedEncodingException e) {
throws IOException { throw new VorbisCommentReaderException(e);
byte[] buffer = new byte[FIRST_PAGE_LENGTH]; }
IOUtils.readFully(input, buffer); }
int i;
for (i = 6; i < buffer.length; i++) {
if (buffer[i - 5] == 'v' && buffer[i - 4] == 'o'
&& buffer[i - 3] == 'r' && buffer[i - 2] == 'b'
&& buffer[i - 1] == 'i' && buffer[i] == 's'
&& buffer[i - 6] == PACKET_TYPE_IDENTIFICATION) {
return true;
}
}
return false;
}
private boolean findCommentHeader(InputStream input) throws IOException { private String readContentVectorKey(InputStream input, long vectorLength) throws IOException {
char[] buffer = new char["vorbis".length() + 1]; StringBuilder builder = new StringBuilder();
for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) { for (int i = 0; i < vectorLength; i++) {
char c = (char) input.read(); char c = (char) input.read();
int dest = -1; if (c == '=') {
switch (c) { return builder.toString();
case PACKET_TYPE_COMMENT: } else {
dest = 0; builder.append(c);
break; }
case 'v': }
dest = 1; return null; // no key found
break; }
case 'o':
dest = 2;
break;
case 'r':
dest = 3;
break;
case 'b':
dest = 4;
break;
case 'i':
dest = 5;
break;
case 's':
dest = 6;
break;
}
if (dest >= 0) {
buffer[dest] = c;
if (buffer[1] == 'v' && buffer[2] == 'o' && buffer[3] == 'r'
&& buffer[4] == 'b' && buffer[5] == 'i'
&& buffer[6] == 's' && buffer[0] == PACKET_TYPE_COMMENT) {
return true;
}
} else {
Arrays.fill(buffer, (char) 0);
}
}
return false;
}
private VorbisCommentHeader readCommentHeader(InputStream input)
throws IOException, VorbisCommentReaderException {
try {
long vendorLength = EndianUtils.readSwappedUnsignedInteger(input);
String vendorName = readUTF8String(input, vendorLength);
long userCommentLength = EndianUtils
.readSwappedUnsignedInteger(input);
return new VorbisCommentHeader(vendorName, userCommentLength);
} catch (UnsupportedEncodingException e) {
throw new VorbisCommentReaderException(e);
}
}
private String readContentVectorKey(InputStream input, long vectorLength)
throws IOException {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < vectorLength; i++) {
char c = (char) input.read();
if (c == '=') {
return builder.toString();
} else {
builder.append(c);
}
}
return null; // no key found
}
} }