Store embedded chapter information

This commit is contained in:
ByteHamster 2020-02-10 11:29:32 +01:00
parent a980281d47
commit 9497a97289
4 changed files with 44 additions and 85 deletions

View File

@ -2,11 +2,8 @@ package de.danoeh.antennapod.core.feed;
import android.database.Cursor;
import android.text.TextUtils;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import java.util.Objects;
public abstract class Chapter extends FeedComponent {
/** Defines starting point in milliseconds. */

View File

@ -4,6 +4,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.util.Log;
import java.util.zip.CheckedOutputStream;
import org.apache.commons.io.IOUtils;
import java.io.BufferedInputStream;
@ -23,6 +24,7 @@ import de.danoeh.antennapod.core.util.id3reader.ID3ReaderException;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentChapterReader;
import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
import org.apache.commons.io.input.CountingInputStream;
/**
* Utility class for getting chapter data from media files.
@ -82,13 +84,12 @@ public class ChapterUtils {
return;
}
Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
InputStream in = null;
CountingInputStream in = null;
try {
URL url = new URL(p.getStreamUrl());
in = url.openStream();
in = new CountingInputStream(url.openStream());
List<Chapter> chapters = readChaptersFrom(in);
if(!chapters.isEmpty()) {
if (!chapters.isEmpty()) {
p.setChapters(chapters);
}
Log.i(TAG, "Chapters loaded");
@ -114,9 +115,9 @@ public class ChapterUtils {
return;
}
InputStream in = null;
CountingInputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(source));
in = new CountingInputStream(new BufferedInputStream(new FileInputStream(source)));
List<Chapter> chapters = readChaptersFrom(in);
if (!chapters.isEmpty()) {
p.setChapters(chapters);
@ -130,7 +131,7 @@ public class ChapterUtils {
}
@NonNull
private static List<Chapter> readChaptersFrom(InputStream in) throws IOException, ID3ReaderException {
private static List<Chapter> readChaptersFrom(CountingInputStream in) throws IOException, ID3ReaderException {
ChapterReader reader = new ChapterReader();
reader.readInputStream(in);
List<Chapter> chapters = reader.getChapters();

View File

@ -1,22 +1,17 @@
package de.danoeh.antennapod.core.util.id3reader;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;
import android.text.TextUtils;
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;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.input.CountingInputStream;
public class ChapterReader extends ID3Reader {
private static final String TAG = "ID3ChapterReader";
@ -24,6 +19,8 @@ public class ChapterReader extends ID3Reader {
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 static final String FRAME_ID_PICTURE = "APIC";
private static final int IMAGE_TYPE_COVER = 3;
private List<Chapter> chapters;
private ID3Chapter currentChapter;
@ -36,8 +33,7 @@ public class ChapterReader extends ID3Reader {
}
@Override
public int onStartFrameHeader(FrameHeader header, InputStream input)
throws IOException, ID3ReaderException {
public int onStartFrameHeader(FrameHeader header, CountingInputStream input) throws IOException, ID3ReaderException {
Log.d(TAG, "header: " + header);
switch (header.getId()) {
case FRAME_ID_CHAPTER:
@ -85,57 +81,37 @@ public class ChapterReader extends ID3Reader {
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
case FRAME_ID_PICTURE:
if (currentChapter != null) {
Log.d(TAG, header.toString());
StringBuilder mime = new StringBuilder();
int read = readString(mime, input, header.getSize());
byte type = (byte) readChars(input, 1)[0];
read++;
StringBuilder description = new StringBuilder();
read += readISOString(description, input, header.getSize()); // Should use same encoding as mime
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();
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.toString());
if (TextUtils.isEmpty(currentChapter.getImageUrl()) || type == IMAGE_TYPE_COVER) {
currentChapter.setImageUrl(link.toString());
}
} else {
// Data contains the picture
int length = header.getSize() - read;
if (TextUtils.isEmpty(currentChapter.getImageUrl()) || type == IMAGE_TYPE_COVER) {
currentChapter.setImageUrl("embedded-image://" + mime.toString()
+ "@" + input.getByteCount() + ":" + length);
}
skipBytes(input, length);
}
return ID3Reader.ACTION_DONT_SKIP;
}
return ID3Reader.ACTION_DONT_SKIP;
break;
}
return super.onStartFrameHeader(header, input);

View File

@ -9,6 +9,7 @@ import java.nio.charset.Charset;
import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
import de.danoeh.antennapod.core.util.id3reader.model.TagHeader;
import org.apache.commons.io.input.CountingInputStream;
/**
* Reads the ID3 Tag of a given file. In order to use this class, you should
@ -33,8 +34,7 @@ public class ID3Reader {
ID3Reader() {
}
public final void readInputStream(InputStream input) throws IOException,
ID3ReaderException {
public final void readInputStream(CountingInputStream input) throws IOException, ID3ReaderException {
int rc;
readerPosition = 0;
char[] tagHeaderSource = readChars(input, HEADER_LENGTH);
@ -101,20 +101,6 @@ 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.
@ -243,8 +229,7 @@ public class ID3Reader {
return ACTION_SKIP;
}
int onStartFrameHeader(FrameHeader header, InputStream input)
throws IOException, ID3ReaderException {
int onStartFrameHeader(FrameHeader header, CountingInputStream input) throws IOException, ID3ReaderException {
return ACTION_SKIP;
}