summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod/util/id3reader
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2012-09-21 19:00:49 +0200
committerdaniel oeh <daniel.oeh@gmail.com>2012-09-21 19:00:49 +0200
commit0eb841db4b2c340c5c5200e5fb655482ee98cfc6 (patch)
tree0d0902e6f4d681f6634a5da7d7d763ddd9ff55ff /src/de/danoeh/antennapod/util/id3reader
parent2ebef34408570b68d7ef9b16afb8887698d2eee3 (diff)
downloadAntennaPod-0eb841db4b2c340c5c5200e5fb655482ee98cfc6.zip
Integrated id3 chapterreader in Antennapod
Diffstat (limited to 'src/de/danoeh/antennapod/util/id3reader')
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/ChapterReader.java90
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/ID3Reader.java182
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/ID3ReaderException.java20
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java18
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/model/Header.java29
-rw-r--r--src/de/danoeh/antennapod/util/id3reader/model/TagHeader.java26
6 files changed, 365 insertions, 0 deletions
diff --git a/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java b/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java
new file mode 100644
index 000000000..3711cb644
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/ChapterReader.java
@@ -0,0 +1,90 @@
+package de.danoeh.antennapod.util.id3reader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.danoeh.antennapod.feed.ID3Chapter;
+import de.danoeh.antennapod.util.id3reader.model.FrameHeader;
+import de.danoeh.antennapod.util.id3reader.model.TagHeader;
+
+public class ChapterReader extends ID3Reader {
+
+ private static final String FRAME_ID_CHAPTER = "CHAP";
+ private static final String FRAME_ID_TITLE = "TIT2";
+
+ private List<ID3Chapter> chapters;
+ private ID3Chapter currentChapter;
+
+ @Override
+ public int onStartTagHeader(TagHeader header) {
+ chapters = new ArrayList<ID3Chapter>();
+ System.out.println(header.toString());
+ return ID3Reader.ACTION_DONT_SKIP;
+ }
+
+ @Override
+ public int onStartFrameHeader(FrameHeader header, InputStream input)
+ throws IOException, ID3ReaderException {
+ System.out.println(header.toString());
+ if (header.getId().equals(FRAME_ID_CHAPTER)) {
+ if (currentChapter != null) {
+ if (!hasId3Chapter(currentChapter)) {
+ chapters.add(currentChapter);
+ currentChapter = null;
+ }
+ }
+ System.out.println("Found chapter");
+ String elementId = readString(input, Integer.MAX_VALUE);
+ char[] startTimeSource = readBytes(input, 4);
+ long startTime = ((int) startTimeSource[0] << 24)
+ | ((int) startTimeSource[1] << 16)
+ | ((int) startTimeSource[2] << 8) | startTimeSource[3];
+ currentChapter = new ID3Chapter(elementId, startTime);
+ skipBytes(input, 12);
+ return ID3Reader.ACTION_DONT_SKIP;
+ } else if (header.getId().equals(FRAME_ID_TITLE)) {
+ if (currentChapter != null && currentChapter.getTitle() == null) {
+ System.out.println("Found title");
+ skipBytes(input, 1);
+ currentChapter
+ .setTitle(readString(input, header.getSize() - 1));
+ return ID3Reader.ACTION_DONT_SKIP;
+ }
+ }
+
+ return super.onStartFrameHeader(header, input);
+ }
+
+ private boolean hasId3Chapter(ID3Chapter chapter) {
+ for (ID3Chapter c : chapters) {
+ if (c.getId3ID().equals(chapter.getId3ID())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onEndTag() {
+ if (currentChapter != null) {
+ if (!hasId3Chapter(currentChapter)) {
+ chapters.add(currentChapter);
+ }
+ }
+ System.out.println("Reached end of tag");
+ if (chapters != null) {
+ for (ID3Chapter c : chapters) {
+ System.out.println(c.toString());
+ }
+ }
+ }
+
+ @Override
+ public void onNoTagHeaderFound() {
+ System.out.println("No tag header found");
+ super.onNoTagHeaderFound();
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java
new file mode 100644
index 000000000..8164170bf
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/ID3Reader.java
@@ -0,0 +1,182 @@
+package de.danoeh.antennapod.util.id3reader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+import de.danoeh.antennapod.util.id3reader.model.FrameHeader;
+import de.danoeh.antennapod.util.id3reader.model.TagHeader;
+
+/**
+ * Reads the ID3 Tag of a given file. In order to use this class, you should
+ * create a subclass of it and overwrite the onStart* - or onEnd* - methods.
+ */
+public class ID3Reader {
+ private static final int HEADER_LENGTH = 10;
+ private static final int ID3_LENGTH = 3;
+ private static final int FRAME_ID_LENGTH = 4;
+
+ protected static final int ACTION_SKIP = 1;
+ protected static final int ACTION_DONT_SKIP = 2;
+
+ protected int readerPosition;
+
+ private static final char[] LITTLE_ENDIAN_BOM = { 0xff, 0xfe };
+ private static final char[] BIG_ENDIAN_BOM = { 0xfe, 0xff };
+
+ public ID3Reader() {
+ }
+
+ public final void readInputStream(InputStream input) throws IOException,
+ ID3ReaderException {
+ int rc;
+ readerPosition = 0;
+ char[] tagHeaderSource = readBytes(input, HEADER_LENGTH);
+ TagHeader tagHeader = createTagHeader(tagHeaderSource);
+ if (tagHeader == null) {
+ onNoTagHeaderFound();
+ } else {
+ rc = onStartTagHeader(tagHeader);
+ if (rc == ACTION_SKIP) {
+ onEndTag();
+ } else {
+ while (readerPosition < tagHeader.getSize()) {
+ FrameHeader frameHeader = createFrameHeader(readBytes(
+ input, HEADER_LENGTH));
+ rc = onStartFrameHeader(frameHeader, input);
+ if (rc == ACTION_SKIP) {
+ skipBytes(input, frameHeader.getSize());
+ }
+ }
+ onEndTag();
+ }
+ }
+ }
+
+ /**
+ * Read a certain number of bytes from the given input stream. This method
+ * changes the readerPosition-attribute.
+ */
+ protected char[] readBytes(InputStream input, int number)
+ throws IOException, ID3ReaderException {
+ char[] header = new char[number];
+ for (int i = 0; i < number; i++) {
+ int b = input.read();
+ readerPosition++;
+ if (b != -1) {
+ header[i] = (char) b;
+ } else {
+ throw new ID3ReaderException("Unexpected end of stream");
+ }
+ }
+ return header;
+ }
+
+ /**
+ * Skip a certain number of bytes on the given input stream. This method
+ * changes the readerPosition-attribute.
+ */
+ protected void skipBytes(InputStream input, int number) throws IOException {
+ int skipped = 0;
+ while (skipped < number) {
+ skipped += input.skip(number - skipped);
+ System.out.println("Skipped = " + skipped);
+ }
+
+ readerPosition += number;
+ }
+
+ private TagHeader createTagHeader(char[] source) throws ID3ReaderException {
+ boolean hasTag = (source[0] == 0x49) && (source[1] == 0x44)
+ && (source[2] == 0x33);
+ if (source.length != HEADER_LENGTH) {
+ throw new ID3ReaderException("Length of header must be "
+ + HEADER_LENGTH);
+ }
+ if (hasTag) {
+ String id = null;
+ id = new String(source, 0, ID3_LENGTH);
+ char version = (char) ((source[3] << 8) | source[4]);
+ byte flags = (byte) source[5];
+ int size = (source[6] << 24) | (source[7] << 16) | (source[8] << 8)
+ | source[9];
+ return new TagHeader(id, size, version, flags);
+ } else {
+ return null;
+ }
+ }
+
+ private FrameHeader createFrameHeader(char[] source)
+ throws ID3ReaderException {
+ if (source.length != HEADER_LENGTH) {
+ throw new ID3ReaderException("Length of header must be "
+ + HEADER_LENGTH);
+ }
+ String id = null;
+ id = new String(source, 0, FRAME_ID_LENGTH);
+ int size = (((int) source[4]) << 24) | (((int) source[5]) << 16)
+ | (((int) source[6]) << 8) | source[7];
+ char flags = (char) ((source[8] << 8) | source[9]);
+ return new FrameHeader(id, size, flags);
+ }
+
+ protected String readString(InputStream input, int max) throws IOException,
+ ID3ReaderException {
+ char[] bom = readBytes(input, 2);
+ if (bom == LITTLE_ENDIAN_BOM || bom == BIG_ENDIAN_BOM) {
+ return readUnicodeString(input, bom, max);
+ } else {
+ PushbackInputStream pi = new PushbackInputStream(input, 2);
+ pi.unread(bom[1]);
+ pi.unread(bom[0]);
+ return readISOString(pi, max);
+ }
+ }
+
+ private String readISOString(InputStream input, int max)
+ throws IOException, ID3ReaderException {
+ int bytesRead = 0;
+ StringBuilder builder = new StringBuilder();
+ char c;
+ while (++bytesRead <= max && (c = (char) input.read()) > 0) {
+ builder.append(c);
+ }
+ return builder.toString();
+ }
+
+ private String readUnicodeString(InputStream input, char[] bom, int max)
+ throws IOException, ID3ReaderException {
+ StringBuffer builder = new StringBuffer();
+ char c1 = (char) input.read();
+ char c2 = (char) input.read();
+ int bytesRead = 2;
+ while ((c1 > 0 && c2 > 0) && ++bytesRead <= max) {
+
+ builder.append(c1);
+ c1 = c2;
+ c2 = (char) input.read();
+ }
+ if (bom == LITTLE_ENDIAN_BOM) {
+ builder.reverse();
+ }
+ return builder.toString();
+ }
+
+ public int onStartTagHeader(TagHeader header) {
+ return ACTION_SKIP;
+ }
+
+ public int onStartFrameHeader(FrameHeader header, InputStream input)
+ throws IOException, ID3ReaderException {
+ return ACTION_SKIP;
+ }
+
+ public void onEndTag() {
+
+ }
+
+ public void onNoTagHeaderFound() {
+
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/ID3ReaderException.java b/src/de/danoeh/antennapod/util/id3reader/ID3ReaderException.java
new file mode 100644
index 000000000..c458540ee
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/ID3ReaderException.java
@@ -0,0 +1,20 @@
+package de.danoeh.antennapod.util.id3reader;
+
+public class ID3ReaderException extends Exception {
+
+ public ID3ReaderException() {
+ }
+
+ public ID3ReaderException(String arg0) {
+ super(arg0);
+ }
+
+ public ID3ReaderException(Throwable arg0) {
+ super(arg0);
+ }
+
+ public ID3ReaderException(String arg0, Throwable arg1) {
+ super(arg0, arg1);
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java b/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java
new file mode 100644
index 000000000..2c0d8e5ba
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/model/FrameHeader.java
@@ -0,0 +1,18 @@
+package de.danoeh.antennapod.util.id3reader.model;
+
+public class FrameHeader extends Header {
+
+ protected char flags;
+
+ public FrameHeader(String id, int size, char flags) {
+ super(id, size);
+ this.flags = flags;
+ }
+
+ @Override
+ public String toString() {
+ return "FrameHeader [flags=" + Integer.toString(flags) + ", id=" + id + ", size=" + size
+ + "]";
+ }
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/model/Header.java b/src/de/danoeh/antennapod/util/id3reader/model/Header.java
new file mode 100644
index 000000000..22d5b6376
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/model/Header.java
@@ -0,0 +1,29 @@
+package de.danoeh.antennapod.util.id3reader.model;
+
+public abstract class Header {
+
+ protected String id;
+ protected int size;
+
+ public Header(String id, int size) {
+ super();
+ this.id = id;
+ this.size = size;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ @Override
+ public String toString() {
+ return "Header [id=" + id + ", size=" + size + "]";
+ }
+
+
+
+}
diff --git a/src/de/danoeh/antennapod/util/id3reader/model/TagHeader.java b/src/de/danoeh/antennapod/util/id3reader/model/TagHeader.java
new file mode 100644
index 000000000..ec99ef14e
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/id3reader/model/TagHeader.java
@@ -0,0 +1,26 @@
+package de.danoeh.antennapod.util.id3reader.model;
+
+public class TagHeader extends Header {
+
+ protected char version;
+ protected byte flags;
+
+ public TagHeader(String id, int size, char version, byte flags) {
+ super(id, size);
+ this.version = version;
+ this.flags = flags;
+ }
+
+ @Override
+ public String toString() {
+ return "TagHeader [version=" + version + ", flags=" + flags + ", id="
+ + id + ", size=" + size + "]";
+ }
+
+ public char getVersion() {
+ return version;
+ }
+
+
+
+}