Added id3 chapter image reader

This commit is contained in:
ByteHamster 2020-02-10 00:14:16 +01:00
parent 5caaaa9e5a
commit a980281d47
2 changed files with 167 additions and 100 deletions

View File

@ -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<Chapter> chapters;
private ID3Chapter currentChapter;
private List<Chapter> 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 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());
@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 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 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
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;
}
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);
@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);
}
}
}
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;
}
@Override
public void onNoTagHeaderFound() {
Log.d(TAG, "No tag header found");
super.onNoTagHeaderFound();
}
return super.onStartFrameHeader(header, input);
}
public List<Chapter> getChapters() {
return chapters;
}
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<Chapter> getChapters() {
return chapters;
}
}

View File

@ -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) {