diff options
author | daniel oeh <daniel.oeh@gmail.com> | 2012-09-21 19:00:49 +0200 |
---|---|---|
committer | daniel oeh <daniel.oeh@gmail.com> | 2012-09-21 19:00:49 +0200 |
commit | 0eb841db4b2c340c5c5200e5fb655482ee98cfc6 (patch) | |
tree | 0d0902e6f4d681f6634a5da7d7d763ddd9ff55ff /src | |
parent | 2ebef34408570b68d7ef9b16afb8887698d2eee3 (diff) | |
download | AntennaPod-0eb841db4b2c340c5c5200e5fb655482ee98cfc6.zip |
Integrated id3 chapterreader in Antennapod
Diffstat (limited to 'src')
9 files changed, 442 insertions, 10 deletions
diff --git a/src/de/danoeh/antennapod/feed/Chapter.java b/src/de/danoeh/antennapod/feed/Chapter.java index b44723d8e..f50053f93 100644 --- a/src/de/danoeh/antennapod/feed/Chapter.java +++ b/src/de/danoeh/antennapod/feed/Chapter.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.feed; -public abstract class Chapter extends FeedComponent{ +public abstract class Chapter extends FeedComponent { /** Defines starting point in milliseconds. */ protected long start; @@ -8,6 +8,11 @@ public abstract class Chapter extends FeedComponent{ protected FeedItem item; protected String link; + public Chapter(long start) { + super(); + this.start = start; + } + public Chapter(long start, String title, FeedItem item, String link) { super(); this.start = start; @@ -34,4 +39,20 @@ public abstract class Chapter extends FeedComponent{ return link; } + public void setStart(long start) { + this.start = start; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setItem(FeedItem item) { + this.item = item; + } + + public void setLink(String link) { + this.link = link; + } + } diff --git a/src/de/danoeh/antennapod/feed/FeedManager.java b/src/de/danoeh/antennapod/feed/FeedManager.java index 526334b2b..1c6d7036f 100644 --- a/src/de/danoeh/antennapod/feed/FeedManager.java +++ b/src/de/danoeh/antennapod/feed/FeedManager.java @@ -374,13 +374,13 @@ public class FeedManager { downloadFeedItem(context, queue.toArray(new FeedItem[queue.size()])); } } - + public void downloadFeedItem(final Context context, FeedItem... items) { boolean autoQueue = PreferenceManager.getDefaultSharedPreferences( context.getApplicationContext()).getBoolean( PodcastApp.PREF_AUTO_QUEUE, true); List<FeedItem> addToQueue = new ArrayList<FeedItem>(); - + for (FeedItem item : items) { if (item.getMedia() != null && !requester.isDownloadingFile(item.getMedia()) @@ -390,7 +390,8 @@ public class FeedManager { } } if (autoQueue) { - addQueueItem(context, addToQueue.toArray(new FeedItem[addToQueue.size()])); + addQueueItem(context, + addToQueue.toArray(new FeedItem[addToQueue.size()])); } } @@ -921,15 +922,24 @@ public class FeedManager { if (chapterCursor.moveToFirst()) { item.setChapters(new ArrayList<Chapter>()); do { - int chapterType = chapterCursor.getInt(PodDBAdapter.KEY_CHAPTER_TYPE_INDEX); + int chapterType = chapterCursor + .getInt(PodDBAdapter.KEY_CHAPTER_TYPE_INDEX); Chapter chapter = null; - long start = chapterCursor.getLong(PodDBAdapter.KEY_CHAPTER_START_INDEX); - String title = chapterCursor.getString(PodDBAdapter.KEY_TITLE_INDEX); - String link = chapterCursor.getString(PodDBAdapter.KEY_CHAPTER_LINK_INDEX); - + long start = chapterCursor + .getLong(PodDBAdapter.KEY_CHAPTER_START_INDEX); + String title = chapterCursor + .getString(PodDBAdapter.KEY_TITLE_INDEX); + String link = chapterCursor + .getString(PodDBAdapter.KEY_CHAPTER_LINK_INDEX); + switch (chapterType) { case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER: - chapter = new SimpleChapter(start, title, item, link); + chapter = new SimpleChapter(start, title, item, + link); + break; + case ID3Chapter.CHAPTERTYPE_ID3CHAPTER: + chapter = new ID3Chapter(start, title, item, + link); break; } chapter.setId(chapterCursor diff --git a/src/de/danoeh/antennapod/feed/ID3Chapter.java b/src/de/danoeh/antennapod/feed/ID3Chapter.java new file mode 100644 index 000000000..6dde7854e --- /dev/null +++ b/src/de/danoeh/antennapod/feed/ID3Chapter.java @@ -0,0 +1,36 @@ +package de.danoeh.antennapod.feed; + +public class ID3Chapter extends Chapter { + public static final int CHAPTERTYPE_ID3CHAPTER = 2; + + /** + * Identifies the chapter in its ID3 tag. This attribute does not have to be + * store in the DB and is only used for parsing. + */ + private String id3ID; + + public ID3Chapter(String id3ID, long start) { + super(start); + this.id3ID = id3ID; + } + + public ID3Chapter(long start, String title, FeedItem item, String link) { + super(start, title, item, link); + } + + @Override + public String toString() { + return "ID3Chapter [id3ID=" + id3ID + ", title=" + title + ", start=" + + start + ", url=" + link + "]"; + } + + @Override + public int getChapterType() { + return CHAPTERTYPE_ID3CHAPTER; + } + + public String getId3ID() { + return id3ID; + } + +} 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; + } + + + +} |