Store embedded chapter information
This commit is contained in:
parent
a980281d47
commit
9497a97289
@ -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. */
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user