From a980281d4794a867e9c972b53c87e6355182a916 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Mon, 10 Feb 2020 00:14:16 +0100 Subject: Added id3 chapter image reader --- .../core/util/id3reader/ChapterReader.java | 260 +++++++++++++-------- .../antennapod/core/util/id3reader/ID3Reader.java | 25 +- 2 files changed, 176 insertions(+), 109 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java index a3f747e09..f7c5bfec7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java @@ -1,11 +1,17 @@ package de.danoeh.antennapod.core.util.id3reader; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.Base64; import android.util.Log; import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.ID3Chapter; +import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader; import de.danoeh.antennapod.core.util.id3reader.model.TagHeader; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URLDecoder; @@ -15,110 +21,158 @@ import java.util.List; public class ChapterReader extends ID3Reader { private static final String TAG = "ID3ChapterReader"; - private static final String FRAME_ID_CHAPTER = "CHAP"; - private static final String FRAME_ID_TITLE = "TIT2"; + private static final String FRAME_ID_CHAPTER = "CHAP"; + private static final String FRAME_ID_TITLE = "TIT2"; private static final String FRAME_ID_LINK = "WXXX"; - private List chapters; - private ID3Chapter currentChapter; - - @Override - public int onStartTagHeader(TagHeader header) { - chapters = new ArrayList<>(); - Log.d(TAG, "header: " + header); - return ID3Reader.ACTION_DONT_SKIP; - } - - @Override - public int onStartFrameHeader(FrameHeader header, InputStream input) - throws IOException, ID3ReaderException { - Log.d(TAG, "header: " + header); - switch (header.getId()) { - case FRAME_ID_CHAPTER: - if (currentChapter != null) { - if (!hasId3Chapter(currentChapter)) { - chapters.add(currentChapter); - Log.d(TAG, "Found chapter: " + currentChapter); - currentChapter = null; - } - } - StringBuilder elementId = new StringBuilder(); - readISOString(elementId, 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.toString(), startTime); - skipBytes(input, 12); - return ID3Reader.ACTION_DONT_SKIP; - case FRAME_ID_TITLE: - if (currentChapter != null && currentChapter.getTitle() == null) { - StringBuilder title = new StringBuilder(); - readString(title, input, header.getSize()); - currentChapter - .setTitle(title.toString()); - Log.d(TAG, "Found title: " + currentChapter.getTitle()); - - return ID3Reader.ACTION_DONT_SKIP; - } - break; - case FRAME_ID_LINK: - if (currentChapter != null) { - // skip description - int descriptionLength = readString(null, input, header.getSize()); - StringBuilder link = new StringBuilder(); - readISOString(link, input, header.getSize() - descriptionLength); - try { - String decodedLink = URLDecoder.decode(link.toString(), "UTF-8"); - currentChapter.setLink(decodedLink); - Log.d(TAG, "Found link: " + currentChapter.getLink()); - } catch (IllegalArgumentException iae) { - Log.w(TAG, "Bad URL found in ID3 data"); - } - - return ID3Reader.ACTION_DONT_SKIP; - } - break; - case "APIC": - Log.d(TAG, header.toString()); - break; - } - - return super.onStartFrameHeader(header, input); - } - - private boolean hasId3Chapter(ID3Chapter chapter) { - for (Chapter c : chapters) { - if (((ID3Chapter) c).getId3ID().equals(chapter.getId3ID())) { - return true; - } - } - return false; - } - - @Override - public void onEndTag() { - if (currentChapter != null) { - if (!hasId3Chapter(currentChapter)) { - chapters.add(currentChapter); - } - } - Log.d(TAG, "Reached end of tag"); - if (chapters != null) { - for (Chapter c : chapters) { - Log.d(TAG, "chapter: " + c); - } - } - } - - @Override - public void onNoTagHeaderFound() { - Log.d(TAG, "No tag header found"); - super.onNoTagHeaderFound(); - } - - public List getChapters() { - return chapters; - } + private List chapters; + private ID3Chapter currentChapter; + + @Override + public int onStartTagHeader(TagHeader header) { + chapters = new ArrayList<>(); + Log.d(TAG, "header: " + header); + return ID3Reader.ACTION_DONT_SKIP; + } + + @Override + public int onStartFrameHeader(FrameHeader header, InputStream input) + throws IOException, ID3ReaderException { + Log.d(TAG, "header: " + header); + switch (header.getId()) { + case FRAME_ID_CHAPTER: + if (currentChapter != null) { + if (!hasId3Chapter(currentChapter)) { + chapters.add(currentChapter); + Log.d(TAG, "Found chapter: " + currentChapter); + currentChapter = null; + } + } + StringBuilder elementId = new StringBuilder(); + readISOString(elementId, input, Integer.MAX_VALUE); + char[] startTimeSource = readChars(input, 4); + long startTime = ((int) startTimeSource[0] << 24) + | ((int) startTimeSource[1] << 16) + | ((int) startTimeSource[2] << 8) | startTimeSource[3]; + currentChapter = new ID3Chapter(elementId.toString(), startTime); + skipBytes(input, 12); + return ID3Reader.ACTION_DONT_SKIP; + case FRAME_ID_TITLE: + if (currentChapter != null && currentChapter.getTitle() == null) { + StringBuilder title = new StringBuilder(); + readString(title, input, header.getSize()); + currentChapter + .setTitle(title.toString()); + Log.d(TAG, "Found title: " + currentChapter.getTitle()); + + return ID3Reader.ACTION_DONT_SKIP; + } + break; + case FRAME_ID_LINK: + if (currentChapter != null) { + // skip description + int descriptionLength = readString(null, input, header.getSize()); + StringBuilder link = new StringBuilder(); + readISOString(link, input, header.getSize() - descriptionLength); + try { + String decodedLink = URLDecoder.decode(link.toString(), "UTF-8"); + currentChapter.setLink(decodedLink); + Log.d(TAG, "Found link: " + currentChapter.getLink()); + } catch (IllegalArgumentException iae) { + Log.w(TAG, "Bad URL found in ID3 data"); + } + + return ID3Reader.ACTION_DONT_SKIP; + } + break; + case "APIC": + Log.d(TAG, header.toString()); + StringBuilder mime = new StringBuilder(); + int read = readString(mime, input, header.getSize()); + byte type = (byte) input.read(); + // $00 Other + // $01 32x32 pixels 'file icon' (PNG only) + // $02 Other file icon + // $03 Cover (front) + // $04 Cover (back) + // $05 Leaflet page + // $06 Media (e.g. label side of CD) + // $07 Lead artist/lead performer/soloist + // $08 Artist/performer + // $09 Conductor + // $0A Band/Orchestra + // $0B Composer + // $0C Lyricist/text writer + // $0D Recording Location + // $0E During recording + // $0F During performance + // $10 Movie/video screen capture + // $11 A bright coloured fish + // $12 Illustration + // $13 Band/artist logotype + // $14 Publisher/Studio logotype + read++; + StringBuilder description = new StringBuilder(); + read += readISOString(description, input, header.getSize()); // Should use encoding from first string + + + Log.d(TAG, "Found apic: " + mime + "," + description); + if (mime.toString().equals("-->")) { + // Data contains a link to a picture + StringBuilder link = new StringBuilder(); + readISOString(link, input, header.getSize()); + Log.d(TAG, "link: " + link); + } else { + // Data contains the picture + byte[] imageData = readBytes(input, header.getSize() - read); + + Bitmap bmp = BitmapFactory.decodeByteArray(imageData, 0, imageData.length); + try (FileOutputStream out = new FileOutputStream(new File(UserPreferences.getDataFolder(null), + "chapter" + chapters.size() + ".jpg"))) { + bmp.compress(Bitmap.CompressFormat.PNG, 100, out); // bmp is your Bitmap instance + // PNG is a lossless format, the compression factor (100) is ignored + } catch (IOException e) { + e.printStackTrace(); + } + } + return ID3Reader.ACTION_DONT_SKIP; + } + + return super.onStartFrameHeader(header, input); + } + + private boolean hasId3Chapter(ID3Chapter chapter) { + for (Chapter c : chapters) { + if (((ID3Chapter) c).getId3ID().equals(chapter.getId3ID())) { + return true; + } + } + return false; + } + + @Override + public void onEndTag() { + if (currentChapter != null) { + if (!hasId3Chapter(currentChapter)) { + chapters.add(currentChapter); + } + } + Log.d(TAG, "Reached end of tag"); + if (chapters != null) { + for (Chapter c : chapters) { + Log.d(TAG, "chapter: " + c); + } + } + } + + @Override + public void onNoTagHeaderFound() { + Log.d(TAG, "No tag header found"); + super.onNoTagHeaderFound(); + } + + public List getChapters() { + return chapters; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java index 3f5993700..85538d94c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java @@ -37,7 +37,7 @@ public class ID3Reader { ID3ReaderException { int rc; readerPosition = 0; - char[] tagHeaderSource = readBytes(input, HEADER_LENGTH); + char[] tagHeaderSource = readChars(input, HEADER_LENGTH); tagHeader = createTagHeader(tagHeaderSource); if (tagHeader == null) { onNoTagHeaderFound(); @@ -47,7 +47,7 @@ public class ID3Reader { onEndTag(); } else { while (readerPosition < tagHeader.getSize()) { - FrameHeader frameHeader = createFrameHeader(readBytes(input, HEADER_LENGTH)); + FrameHeader frameHeader = createFrameHeader(readChars(input, HEADER_LENGTH)); if (checkForNullString(frameHeader.getId())) { break; } @@ -84,11 +84,10 @@ public class ID3Reader { } /** - * Read a certain number of bytes from the given input stream. This method + * Read a certain number of chars from the given input stream. This method * changes the readerPosition-attribute. */ - char[] readBytes(InputStream input, int number) - throws IOException, ID3ReaderException { + char[] readChars(InputStream input, int number) throws IOException, ID3ReaderException { char[] header = new char[number]; for (int i = 0; i < number; i++) { int b = input.read(); @@ -102,6 +101,20 @@ public class ID3Reader { return header; } + byte[] readBytes(InputStream input, int number) throws IOException, ID3ReaderException { + byte[] header = new byte[number]; + for (int i = 0; i < number; i++) { + int b = input.read(); + readerPosition++; + if (b != -1) { + header[i] = (byte) 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. @@ -168,7 +181,7 @@ public class ID3Reader { protected int readString(StringBuilder buffer, InputStream input, int max) throws IOException, ID3ReaderException { if (max > 0) { - char[] encoding = readBytes(input, 1); + char[] encoding = readChars(input, 1); max--; if (encoding[0] == ENCODING_UTF16_WITH_BOM || encoding[0] == ENCODING_UTF16_WITHOUT_BOM) { -- cgit v1.2.3