summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2021-08-28 09:52:45 +0200
committerByteHamster <info@bytehamster.com>2021-08-28 10:59:26 +0200
commitca64739f363e585758b6ada6cc4e6c9d43191724 (patch)
tree3b5b6634792a3997d9302053e628ec8cda205ff5 /core
parentddae5e2278fe6b6e950576cdc460ec7ffe761d5d (diff)
downloadAntennaPod-ca64739f363e585758b6ada6cc4e6c9d43191724.zip
Moved media file parser to its own module
Diffstat (limited to 'core')
-rw-r--r--core/build.gradle1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java38
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java89
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java73
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java119
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java213
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3ReaderException.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/FrameHeader.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/Header.java29
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/TagHeader.java25
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/OggInputStream.java81
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java100
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentHeader.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java166
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReaderException.java21
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/id3reader/ChapterReaderTest.java209
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/id3reader/Id3ReaderTest.java151
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReaderTest.java44
-rw-r--r--core/src/test/resources/media-parser/auphonic.m4abin114657 -> 0 bytes
-rw-r--r--core/src/test/resources/media-parser/auphonic.mp3bin143695 -> 0 bytes
-rw-r--r--core/src/test/resources/media-parser/auphonic.oggbin6565 -> 0 bytes
-rw-r--r--core/src/test/resources/media-parser/auphonic.opusbin4189 -> 0 bytes
-rw-r--r--core/src/test/resources/media-parser/hindenburg-journalist-pro.m4abin23315 -> 0 bytes
-rw-r--r--core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3bin206098 -> 0 bytes
-rw-r--r--core/src/test/resources/media-parser/mp3chaps-py.mp3bin123247 -> 0 bytes
-rw-r--r--core/src/test/resources/media-parser/ultraschall5.mp3bin5903309 -> 0 bytes
30 files changed, 9 insertions, 1420 deletions
diff --git a/core/build.gradle b/core/build.gradle
index 121b4311e..b0eb1d51e 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -25,6 +25,7 @@ dependencies {
implementation project(':net:sync:gpoddernet')
implementation project(':net:sync:model')
implementation project(':parser:feed')
+ implementation project(':parser:media')
implementation project(':ui:app-start-intent')
implementation project(':ui:common')
implementation project(':ui:png-icons')
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java
deleted file mode 100644
index 56d63c32e..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import de.danoeh.antennapod.model.feed.Chapter;
-
-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, String link, String imageUrl) {
- super(start, title, link, imageUrl);
- }
-
- @Override
- public String toString() {
- return "ID3Chapter [id3ID=" + id3ID + ", title=" + getTitle() + ", start="
- + getStart() + ", url=" + getLink() + "]";
- }
-
- @Override
- public int getChapterType() {
- return CHAPTERTYPE_ID3CHAPTER;
- }
-
- public String getId3ID() {
- return id3ID;
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
deleted file mode 100644
index 6e0b6859d..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import java.util.concurrent.TimeUnit;
-
-import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
-import de.danoeh.antennapod.model.feed.Chapter;
-
-public class VorbisCommentChapter extends Chapter {
- public static final int CHAPTERTYPE_VORBISCOMMENT_CHAPTER = 3;
-
- private static final int CHAPTERXXX_LENGTH = "chapterxxx".length();
-
- private int vorbisCommentId;
-
- public VorbisCommentChapter(int vorbisCommentId) {
- this.vorbisCommentId = vorbisCommentId;
- }
-
- public VorbisCommentChapter(long start, String title, String link, String imageUrl) {
- super(start, title, link, imageUrl);
- }
-
- @Override
- public String toString() {
- return "VorbisCommentChapter [id=" + getId() + ", title=" + getTitle()
- + ", link=" + getLink() + ", start=" + getStart() + "]";
- }
-
- public static long getStartTimeFromValue(String value)
- throws VorbisCommentReaderException {
- String[] parts = value.split(":");
- if (parts.length >= 3) {
- try {
- long hours = TimeUnit.MILLISECONDS.convert(
- Long.parseLong(parts[0]), TimeUnit.HOURS);
- long minutes = TimeUnit.MILLISECONDS.convert(
- Long.parseLong(parts[1]), TimeUnit.MINUTES);
- if (parts[2].contains("-->")) {
- parts[2] = parts[2].substring(0, parts[2].indexOf("-->"));
- }
- long seconds = TimeUnit.MILLISECONDS.convert(
- ((long) Float.parseFloat(parts[2])), TimeUnit.SECONDS);
- return hours + minutes + seconds;
- } catch (NumberFormatException e) {
- throw new VorbisCommentReaderException(e);
- }
- } else {
- throw new VorbisCommentReaderException("Invalid time string");
- }
- }
-
- /**
- * Return the id of a vorbiscomment chapter from a string like CHAPTERxxx*
- *
- * @return the id of the chapter key or -1 if the id couldn't be read.
- * @throws VorbisCommentReaderException
- * */
- public static int getIDFromKey(String key) throws VorbisCommentReaderException {
- if (key.length() >= CHAPTERXXX_LENGTH) { // >= CHAPTERxxx
- try {
- String strId = key.substring(8, 10);
- return Integer.parseInt(strId);
- } catch (NumberFormatException e) {
- throw new VorbisCommentReaderException(e);
- }
- }
- throw new VorbisCommentReaderException("key is too short (" + key + ")");
- }
-
- /**
- * Get the string that comes after 'CHAPTERxxx', for example 'name' or
- * 'url'.
- */
- public static String getAttributeTypeFromKey(String key) {
- if (key.length() > CHAPTERXXX_LENGTH) {
- return key.substring(CHAPTERXXX_LENGTH);
- }
- return null;
- }
-
- @Override
- public int getChapterType() {
- return CHAPTERTYPE_VORBISCOMMENT_CHAPTER;
- }
-
- public int getVorbisCommentId() {
- return vorbisCommentId;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
index 8ad04cb47..defe6c9f8 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
@@ -14,7 +14,7 @@ import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
import com.bumptech.glide.load.model.StringLoader;
import com.bumptech.glide.module.AppGlideModule;
-import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
+import de.danoeh.antennapod.model.feed.EmbeddedChapterImage;
import java.io.InputStream;
import com.bumptech.glide.request.RequestOptions;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
index d6d63fed0..63126405d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
@@ -10,7 +10,7 @@ import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import com.bumptech.glide.signature.ObjectKey;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
-import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
+import de.danoeh.antennapod.model.feed.EmbeddedChapterImage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java
index 61613a25a..5fa376129 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java
@@ -3,10 +3,10 @@ package de.danoeh.antennapod.core.storage.mapper;
import android.database.Cursor;
import androidx.annotation.NonNull;
import de.danoeh.antennapod.model.feed.Chapter;
-import de.danoeh.antennapod.core.feed.ID3Chapter;
import de.danoeh.antennapod.parser.feed.element.SimpleChapter;
-import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.parser.media.id3.ID3Chapter;
+import de.danoeh.antennapod.parser.media.vorbis.VorbisCommentChapter;
/**
* Converts a {@link Cursor} to a {@link Chapter} object.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
index 1bc2c13ee..4092087f4 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
@@ -11,11 +11,11 @@ import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.comparator.ChapterStartTimeComparator;
-import de.danoeh.antennapod.core.util.id3reader.ChapterReader;
-import de.danoeh.antennapod.core.util.id3reader.ID3ReaderException;
+import de.danoeh.antennapod.parser.media.id3.ChapterReader;
+import de.danoeh.antennapod.parser.media.id3.ID3ReaderException;
import de.danoeh.antennapod.model.playback.Playable;
-import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentChapterReader;
-import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
+import de.danoeh.antennapod.parser.media.vorbis.VorbisCommentChapterReader;
+import de.danoeh.antennapod.parser.media.vorbis.VorbisCommentReaderException;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.io.input.CountingInputStream;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java b/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java
deleted file mode 100644
index a3966a24c..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import android.text.TextUtils;
-import de.danoeh.antennapod.model.playback.Playable;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class EmbeddedChapterImage {
- private static final Pattern EMBEDDED_IMAGE_MATCHER = Pattern.compile("embedded-image://(\\d+)/(\\d+)");
-
- private final int position;
- private final int length;
- private final String imageUrl;
- private final Playable media;
-
- public EmbeddedChapterImage(Playable media, String imageUrl) {
- this.media = media;
- this.imageUrl = imageUrl;
- Matcher m = EMBEDDED_IMAGE_MATCHER.matcher(imageUrl);
- if (m.find()) {
- this.position = Integer.parseInt(m.group(1));
- this.length = Integer.parseInt(m.group(2));
- } else {
- throw new IllegalArgumentException("Not an embedded chapter");
- }
- }
-
- public static String makeUrl(int position, int length) {
- return "embedded-image://" + position + "/" + length;
- }
-
- public int getPosition() {
- return position;
- }
-
- public int getLength() {
- return length;
- }
-
- public Playable getMedia() {
- return media;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- EmbeddedChapterImage that = (EmbeddedChapterImage) o;
- return TextUtils.equals(imageUrl, that.imageUrl);
- }
-
- @Override
- public int hashCode() {
- return imageUrl.hashCode();
- }
-
- private static boolean isEmbeddedChapterImage(String imageUrl) {
- return EMBEDDED_IMAGE_MATCHER.matcher(imageUrl).matches();
- }
-
- public static Object getModelFor(Playable media, int chapter) {
- String imageUrl = media.getChapters().get(chapter).getImageUrl();
- if (isEmbeddedChapterImage(imageUrl)) {
- return new EmbeddedChapterImage(media, imageUrl);
- } else {
- return imageUrl;
- }
- }
-}
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
deleted file mode 100644
index adc38b0d2..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package de.danoeh.antennapod.core.util.id3reader;
-
-import android.text.TextUtils;
-import android.util.Log;
-import androidx.annotation.NonNull;
-import de.danoeh.antennapod.model.feed.Chapter;
-import de.danoeh.antennapod.core.feed.ID3Chapter;
-import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
-import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
-import org.apache.commons.io.input.CountingInputStream;
-
-import java.io.IOException;
-import java.net.URLDecoder;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Reads ID3 chapters.
- * See https://id3.org/id3v2-chapters-1.0
- */
-public class ChapterReader extends ID3Reader {
- private static final String TAG = "ID3ChapterReader";
-
- public static final String FRAME_ID_CHAPTER = "CHAP";
- public static final String FRAME_ID_TITLE = "TIT2";
- public static final String FRAME_ID_LINK = "WXXX";
- public static final String FRAME_ID_PICTURE = "APIC";
- public static final String MIME_IMAGE_URL = "-->";
- public static final int IMAGE_TYPE_COVER = 3;
-
- private final List<Chapter> chapters = new ArrayList<>();
-
- public ChapterReader(CountingInputStream input) {
- super(input);
- }
-
- @Override
- protected void readFrame(@NonNull FrameHeader frameHeader) throws IOException, ID3ReaderException {
- if (FRAME_ID_CHAPTER.equals(frameHeader.getId())) {
- Log.d(TAG, "Handling frame: " + frameHeader.toString());
- Chapter chapter = readChapter(frameHeader);
- Log.d(TAG, "Chapter done: " + chapter);
- chapters.add(chapter);
- } else {
- super.readFrame(frameHeader);
- }
- }
-
- public Chapter readChapter(@NonNull FrameHeader frameHeader) throws IOException, ID3ReaderException {
- int chapterStartedPosition = getPosition();
- String elementId = readIsoStringNullTerminated(100);
- long startTime = readInt();
- skipBytes(12); // Ignore end time, start offset, end offset
- ID3Chapter chapter = new ID3Chapter(elementId, startTime);
-
- // Read sub-frames
- while (getPosition() < chapterStartedPosition + frameHeader.getSize()) {
- FrameHeader subFrameHeader = readFrameHeader();
- readChapterSubFrame(subFrameHeader, chapter);
- }
- return chapter;
- }
-
- public void readChapterSubFrame(@NonNull FrameHeader frameHeader, @NonNull Chapter chapter)
- throws IOException, ID3ReaderException {
- Log.d(TAG, "Handling subframe: " + frameHeader.toString());
- int frameStartPosition = getPosition();
- switch (frameHeader.getId()) {
- case FRAME_ID_TITLE:
- chapter.setTitle(readEncodingAndString(frameHeader.getSize()));
- Log.d(TAG, "Found title: " + chapter.getTitle());
- break;
- case FRAME_ID_LINK:
- readEncodingAndString(frameHeader.getSize()); // skip description
- String url = readIsoStringNullTerminated(frameStartPosition + frameHeader.getSize() - getPosition());
- try {
- String decodedLink = URLDecoder.decode(url, "ISO-8859-1");
- chapter.setLink(decodedLink);
- Log.d(TAG, "Found link: " + chapter.getLink());
- } catch (IllegalArgumentException iae) {
- Log.w(TAG, "Bad URL found in ID3 data");
- }
- break;
- case FRAME_ID_PICTURE:
- byte encoding = readByte();
- String mime = readEncodedString(encoding, frameHeader.getSize());
- byte type = readByte();
- String description = readEncodedString(encoding, frameHeader.getSize());
- Log.d(TAG, "Found apic: " + mime + "," + description);
- if (MIME_IMAGE_URL.equals(mime)) {
- String link = readIsoStringNullTerminated(frameHeader.getSize());
- Log.d(TAG, "Link: " + link);
- if (TextUtils.isEmpty(chapter.getImageUrl()) || type == IMAGE_TYPE_COVER) {
- chapter.setImageUrl(link);
- }
- } else {
- int alreadyConsumed = getPosition() - frameStartPosition;
- int rawImageDataLength = frameHeader.getSize() - alreadyConsumed;
- if (TextUtils.isEmpty(chapter.getImageUrl()) || type == IMAGE_TYPE_COVER) {
- chapter.setImageUrl(EmbeddedChapterImage.makeUrl(getPosition(), rawImageDataLength));
- }
- }
- break;
- default:
- Log.d(TAG, "Unknown chapter sub-frame.");
- break;
- }
-
- // Skip garbage to fill frame completely
- // This also asserts that we are not reading too many bytes from this frame.
- int alreadyConsumed = getPosition() - frameStartPosition;
- skipBytes(frameHeader.getSize() - alreadyConsumed);
- }
-
- public List<Chapter> 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
deleted file mode 100644
index b7baaa8aa..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package de.danoeh.antennapod.core.util.id3reader;
-
-import android.util.Log;
-import androidx.annotation.NonNull;
-import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
-import de.danoeh.antennapod.core.util.id3reader.model.TagHeader;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.io.input.CountingInputStream;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.MalformedInputException;
-
-/**
- * Reads the ID3 Tag of a given file.
- * See https://id3.org/id3v2.3.0
- */
-public class ID3Reader {
- private static final String TAG = "ID3Reader";
- private static final int FRAME_ID_LENGTH = 4;
- public static final byte ENCODING_ISO = 0;
- public static final byte ENCODING_UTF16_WITH_BOM = 1;
- public static final byte ENCODING_UTF16_WITHOUT_BOM = 2;
- public static final byte ENCODING_UTF8 = 3;
-
- private TagHeader tagHeader;
- private final CountingInputStream inputStream;
-
- public ID3Reader(CountingInputStream input) {
- inputStream = input;
- }
-
- public void readInputStream() throws IOException, ID3ReaderException {
- tagHeader = readTagHeader();
- int tagContentStartPosition = getPosition();
- while (getPosition() < tagContentStartPosition + tagHeader.getSize()) {
- FrameHeader frameHeader = readFrameHeader();
- if (frameHeader.getId().charAt(0) < '0' || frameHeader.getId().charAt(0) > 'z') {
- Log.d(TAG, "Stopping because of invalid frame: " + frameHeader.toString());
- return;
- }
- readFrame(frameHeader);
- }
- }
-
- protected void readFrame(@NonNull FrameHeader frameHeader) throws IOException, ID3ReaderException {
- Log.d(TAG, "Skipping frame: " + frameHeader.toString());
- skipBytes(frameHeader.getSize());
- }
-
- int getPosition() {
- return inputStream.getCount();
- }
-
- /**
- * Skip a certain number of bytes on the given input stream.
- */
- void skipBytes(int number) throws IOException, ID3ReaderException {
- if (number < 0) {
- throw new ID3ReaderException("Trying to read a negative number of bytes");
- }
- IOUtils.skipFully(inputStream, number);
- }
-
- byte readByte() throws IOException {
- return (byte) inputStream.read();
- }
-
- short readShort() throws IOException {
- char firstByte = (char) inputStream.read();
- char secondByte = (char) inputStream.read();
- return (short) ((firstByte << 8) | secondByte);
- }
-
- int readInt() throws IOException {
- char firstByte = (char) inputStream.read();
- char secondByte = (char) inputStream.read();
- char thirdByte = (char) inputStream.read();
- char fourthByte = (char) inputStream.read();
- return (firstByte << 24) | (secondByte << 16) | (thirdByte << 8) | fourthByte;
- }
-
- void expectChar(char expected) throws ID3ReaderException, IOException {
- char read = (char) inputStream.read();
- if (read != expected) {
- throw new ID3ReaderException("Expected " + expected + " and got " + read);
- }
- }
-
- @NonNull
- TagHeader readTagHeader() throws ID3ReaderException, IOException {
- expectChar('I');
- expectChar('D');
- expectChar('3');
- short version = readShort();
- byte flags = readByte();
- int size = unsynchsafe(readInt());
- if ((flags & 0b01000000) != 0) {
- int extendedHeaderSize = readInt();
- skipBytes(extendedHeaderSize - 4);
- }
- return new TagHeader("ID3", size, version, flags);
- }
-
- @NonNull
- FrameHeader readFrameHeader() throws IOException {
- String id = readIsoStringFixed(FRAME_ID_LENGTH);
- int size = readInt();
- if (tagHeader != null && tagHeader.getVersion() >= 0x0400) {
- size = unsynchsafe(size);
- }
- short flags = readShort();
- return new FrameHeader(id, size, flags);
- }
-
- private int unsynchsafe(int in) {
- int out = 0;
- int mask = 0x7F000000;
-
- while (mask != 0) {
- out >>= 1;
- out |= in & mask;
- mask >>= 8;
- }
-
- return out;
- }
-
- /**
- * Reads a null-terminated string with encoding.
- */
- protected String readEncodingAndString(int max) throws IOException {
- byte encoding = readByte();
- return readEncodedString(encoding, max - 1);
- }
-
- @SuppressWarnings("CharsetObjectCanBeUsed")
- protected String readIsoStringFixed(int length) throws IOException {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- int bytesRead = 0;
- while (bytesRead < length) {
- bytes.write(readByte());
- bytesRead++;
- }
- return Charset.forName("ISO-8859-1").newDecoder().decode(ByteBuffer.wrap(bytes.toByteArray())).toString();
- }
-
- protected String readIsoStringNullTerminated(int max) throws IOException {
- return readEncodedString(ENCODING_ISO, max);
- }
-
- @SuppressWarnings("CharsetObjectCanBeUsed")
- String readEncodedString(int encoding, int max) throws IOException {
- if (encoding == ENCODING_UTF16_WITH_BOM || encoding == ENCODING_UTF16_WITHOUT_BOM) {
- return readEncodedString2(Charset.forName("UTF-16"), max);
- } else if (encoding == ENCODING_UTF8) {
- return readEncodedString2(Charset.forName("UTF-8"), max);
- } else {
- return readEncodedString1(Charset.forName("ISO-8859-1"), max);
- }
- }
-
- /**
- * Reads chars where the encoding uses 1 char per symbol.
- */
- private String readEncodedString1(Charset charset, int max) throws IOException {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- int bytesRead = 0;
- while (bytesRead < max) {
- byte c = readByte();
- bytesRead++;
- if (c == 0) {
- break;
- }
- bytes.write(c);
- }
- return charset.newDecoder().decode(ByteBuffer.wrap(bytes.toByteArray())).toString();
- }
-
- /**
- * Reads chars where the encoding uses 2 chars per symbol.
- */
- private String readEncodedString2(Charset charset, int max) throws IOException {
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- int bytesRead = 0;
- boolean foundEnd = false;
- while (bytesRead + 1 < max) {
- byte c1 = readByte();
- byte c2 = readByte();
- if (c1 == 0 && c2 == 0) {
- foundEnd = true;
- break;
- }
- bytesRead += 2;
- bytes.write(c1);
- bytes.write(c2);
- }
- if (!foundEnd && bytesRead < max) {
- // Last character
- byte c = readByte();
- if (c != 0) {
- bytes.write(c);
- }
- }
- try {
- return charset.newDecoder().decode(ByteBuffer.wrap(bytes.toByteArray())).toString();
- } catch (MalformedInputException e) {
- return "";
- }
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3ReaderException.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3ReaderException.java
deleted file mode 100644
index 7a68bb5ce..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3ReaderException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package de.danoeh.antennapod.core.util.id3reader;
-
-public class ID3ReaderException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public ID3ReaderException(String message) {
- super(message);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/FrameHeader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/FrameHeader.java
deleted file mode 100644
index e4af89a86..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/FrameHeader.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package de.danoeh.antennapod.core.util.id3reader.model;
-
-import androidx.annotation.NonNull;
-
-public class FrameHeader extends Header {
- private final short flags;
-
- public FrameHeader(String id, int size, short flags) {
- super(id, size);
- this.flags = flags;
- }
-
- @Override
- @NonNull
- public String toString() {
- return String.format("FrameHeader [flags=%s, id=%s, size=%s]", Integer.toBinaryString(flags), id, size);
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/Header.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/Header.java
deleted file mode 100644
index 29185748f..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/Header.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package de.danoeh.antennapod.core.util.id3reader.model;
-
-public abstract class Header {
-
- final String id;
- final int size;
-
- 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/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/TagHeader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/TagHeader.java
deleted file mode 100644
index 2590db029..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/TagHeader.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package de.danoeh.antennapod.core.util.id3reader.model;
-
-import androidx.annotation.NonNull;
-
-public class TagHeader extends Header {
- private final short version;
- private final byte flags;
-
- public TagHeader(String id, int size, short version, byte flags) {
- super(id, size);
- this.version = version;
- this.flags = flags;
- }
-
- @Override
- @NonNull
- public String toString() {
- return "TagHeader [version=" + version + ", flags=" + flags + ", id="
- + id + ", size=" + size + "]";
- }
-
- public short getVersion() {
- return version;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/OggInputStream.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/OggInputStream.java
deleted file mode 100644
index 9277af6e6..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/OggInputStream.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package de.danoeh.antennapod.core.util.vorbiscommentreader;
-
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-
-class OggInputStream extends InputStream {
- private final InputStream input;
-
- /** True if OggInputStream is currently inside an Ogg page. */
- private boolean isInPage;
- private long bytesLeft;
-
- public OggInputStream(InputStream input) {
- super();
- isInPage = false;
- this.input = input;
- }
-
- @Override
- public int read() throws IOException {
- if (!isInPage) {
- readOggPage();
- }
-
- if (isInPage && bytesLeft > 0) {
- int result = input.read();
- bytesLeft -= 1;
- if (bytesLeft == 0) {
- isInPage = false;
- }
- return result;
- }
- return -1;
- }
-
- private void readOggPage() throws IOException {
- // find OggS
- int[] buffer = new int[4];
- int c;
- boolean isInOggS = false;
- while ((c = input.read()) != -1) {
- switch (c) {
- case 'O':
- isInOggS = true;
- buffer[0] = c;
- break;
- case 'g':
- if (buffer[1] != c) {
- buffer[1] = c;
- } else {
- buffer[2] = c;
- }
- break;
- case 'S':
- buffer[3] = c;
- break;
- default:
- if (isInOggS) {
- Arrays.fill(buffer, 0);
- isInOggS = false;
- }
- }
- if (buffer[0] == 'O' && buffer[1] == 'g' && buffer[2] == 'g'
- && buffer[3] == 'S') {
- break;
- }
- }
- // read segments
- IOUtils.skipFully(input, 22);
- bytesLeft = 0;
- int numSegments = input.read();
- for (int i = 0; i < numSegments; i++) {
- bytesLeft += input.read();
- }
- isInPage = true;
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
deleted file mode 100644
index 26955effc..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package de.danoeh.antennapod.core.util.vorbiscommentreader;
-
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.model.feed.Chapter;
-import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
-
-public class VorbisCommentChapterReader extends VorbisCommentReader {
- private static final String TAG = "VorbisCommentChptrReadr";
-
- private static final String CHAPTER_KEY = "chapter\\d\\d\\d.*";
- private static final String CHAPTER_ATTRIBUTE_TITLE = "name";
- private static final String CHAPTER_ATTRIBUTE_LINK = "url";
-
- private List<Chapter> chapters;
-
- public VorbisCommentChapterReader() {
- }
-
- @Override
- public void onVorbisCommentFound() {
- System.out.println("Vorbis comment found");
- }
-
- @Override
- public void onVorbisCommentHeaderFound(VorbisCommentHeader header) {
- chapters = new ArrayList<>();
- System.out.println(header.toString());
- }
-
- @Override
- public boolean onContentVectorKey(String content) {
- return content.matches(CHAPTER_KEY);
- }
-
- @Override
- public void onContentVectorValue(String key, String value) throws VorbisCommentReaderException {
- if (BuildConfig.DEBUG) {
- Log.d(TAG, "Key: " + key + ", value: " + value);
- }
- String attribute = VorbisCommentChapter.getAttributeTypeFromKey(key);
- int id = VorbisCommentChapter.getIDFromKey(key);
- Chapter chapter = getChapterById(id);
- if (attribute == null) {
- if (getChapterById(id) == null) {
- // new chapter
- long start = VorbisCommentChapter.getStartTimeFromValue(value);
- chapter = new VorbisCommentChapter(id);
- chapter.setStart(start);
- chapters.add(chapter);
- } else {
- throw new VorbisCommentReaderException("Found chapter with duplicate ID (" + key + ", " + value + ")");
- }
- } else if (attribute.equals(CHAPTER_ATTRIBUTE_TITLE)) {
- if (chapter != null) {
- chapter.setTitle(value);
- }
- } else if (attribute.equals(CHAPTER_ATTRIBUTE_LINK)) {
- if (chapter != null) {
- chapter.setLink(value);
- }
- }
- }
-
- @Override
- public void onNoVorbisCommentFound() {
- System.out.println("No vorbis comment found");
- }
-
- @Override
- public void onEndOfComment() {
- System.out.println("End of comment");
- for (Chapter c : chapters) {
- System.out.println(c.toString());
- }
- }
-
- @Override
- public void onError(VorbisCommentReaderException exception) {
- exception.printStackTrace();
- }
-
- private Chapter getChapterById(long id) {
- for (Chapter c : chapters) {
- if (((VorbisCommentChapter) c).getVorbisCommentId() == id) {
- return c;
- }
- }
- return null;
- }
-
- public List<Chapter> getChapters() {
- return chapters;
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentHeader.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentHeader.java
deleted file mode 100644
index ff7508390..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentHeader.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package de.danoeh.antennapod.core.util.vorbiscommentreader;
-class VorbisCommentHeader {
- private final String vendorString;
- private final long userCommentLength;
-
- public VorbisCommentHeader(String vendorString, long userCommentLength) {
- super();
- this.vendorString = vendorString;
- this.userCommentLength = userCommentLength;
- }
-
- @Override
- public String toString() {
- return "VorbisCommentHeader [vendorString=" + vendorString
- + ", userCommentLength=" + userCommentLength + "]";
- }
-
- public String getVendorString() {
- return vendorString;
- }
-
- public long getUserCommentLength() {
- return userCommentLength;
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java
deleted file mode 100644
index e910e2be4..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java
+++ /dev/null
@@ -1,166 +0,0 @@
-package de.danoeh.antennapod.core.util.vorbiscommentreader;
-
-import androidx.annotation.NonNull;
-import org.apache.commons.io.EndianUtils;
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-
-public abstract class VorbisCommentReader {
- /** Length of first page in an ogg file in bytes. */
- private static final int FIRST_OGG_PAGE_LENGTH = 58;
- private static final int FIRST_OPUS_PAGE_LENGTH = 47;
- private static final int SECOND_PAGE_MAX_LENGTH = 64 * 1024 * 1024;
- private static final int PACKET_TYPE_IDENTIFICATION = 1;
- private static final int PACKET_TYPE_COMMENT = 3;
-
- /** Called when Reader finds identification header. */
- protected abstract void onVorbisCommentFound();
-
- protected abstract void onVorbisCommentHeaderFound(VorbisCommentHeader header);
-
- /**
- * Is called every time the Reader finds a content vector. The handler
- * should return true if it wants to handle the content vector.
- */
- protected abstract boolean onContentVectorKey(String content);
-
- /**
- * Is called if onContentVectorKey returned true for the key.
- */
- protected abstract void onContentVectorValue(String key, String value) throws VorbisCommentReaderException;
-
- protected abstract void onNoVorbisCommentFound();
-
- protected abstract void onEndOfComment();
-
- protected abstract void onError(VorbisCommentReaderException exception);
-
- public void readInputStream(InputStream input) throws VorbisCommentReaderException {
- try {
- // look for identification header
- if (findIdentificationHeader(input)) {
- onVorbisCommentFound();
- input = new OggInputStream(input);
- if (findCommentHeader(input)) {
- VorbisCommentHeader commentHeader = readCommentHeader(input);
- onVorbisCommentHeaderFound(commentHeader);
- for (int i = 0; i < commentHeader.getUserCommentLength(); i++) {
- readUserComment(input);
- }
- onEndOfComment();
- } else {
- onError(new VorbisCommentReaderException("No comment header found"));
- }
- } else {
- onNoVorbisCommentFound();
- }
- } catch (IOException e) {
- onError(new VorbisCommentReaderException(e));
- }
- }
-
- private void readUserComment(InputStream input) throws VorbisCommentReaderException {
- try {
- long vectorLength = EndianUtils.readSwappedUnsignedInteger(input);
- String key = readContentVectorKey(input, vectorLength).toLowerCase();
- boolean readValue = onContentVectorKey(key);
- if (readValue) {
- String value = readUtf8String(input, (int) (vectorLength - key.length() - 1));
- onContentVectorValue(key, value);
- } else {
- IOUtils.skipFully(input, vectorLength - key.length() - 1);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- private String readUtf8String(InputStream input, long length) throws IOException {
- byte[] buffer = new byte[(int) length];
- IOUtils.readFully(input, buffer);
- Charset charset = Charset.forName("UTF-8");
- return charset.newDecoder().decode(ByteBuffer.wrap(buffer)).toString();
- }
-
- /**
- * Looks for an identification header in the first page of the file. If an
- * identification header is found, it will be skipped completely and the
- * method will return true, otherwise false.
- */
- private boolean findIdentificationHeader(InputStream input) throws IOException {
- byte[] buffer = new byte[FIRST_OPUS_PAGE_LENGTH];
- IOUtils.readFully(input, buffer);
- final byte[] oggIdentificationHeader = new byte[]{ PACKET_TYPE_IDENTIFICATION, 'v', 'o', 'r', 'b', 'i', 's' };
- for (int i = 6; i < buffer.length; i++) {
- if (bufferMatches(buffer, oggIdentificationHeader, i)) {
- IOUtils.skip(input, FIRST_OGG_PAGE_LENGTH - FIRST_OPUS_PAGE_LENGTH);
- return true;
- } else if (bufferMatches(buffer, "OpusHead".getBytes(), i)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean findCommentHeader(InputStream input) throws IOException {
- byte[] buffer = new byte[64]; // Enough space for some bytes. Used circularly.
- final byte[] oggCommentHeader = new byte[]{ PACKET_TYPE_COMMENT, 'v', 'o', 'r', 'b', 'i', 's' };
- for (int bytesRead = 0; bytesRead < SECOND_PAGE_MAX_LENGTH; bytesRead++) {
- buffer[bytesRead % buffer.length] = (byte) input.read();
- if (bufferMatches(buffer, oggCommentHeader, bytesRead)) {
- return true;
- } else if (bufferMatches(buffer, "OpusTags".getBytes(), bytesRead)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Reads backwards in haystack, starting at position. Checks if the bytes match needle.
- * Uses haystack circularly, so when reading at (-1), it reads at (length - 1).
- */
- boolean bufferMatches(byte[] haystack, byte[] needle, int position) {
- for (int i = 0; i < needle.length; i++) {
- int posInHaystack = position - i;
- while (posInHaystack < 0) {
- posInHaystack += haystack.length;
- }
- posInHaystack = posInHaystack % haystack.length;
- if (haystack[posInHaystack] != needle[needle.length - 1 - i]) {
- return false;
- }
- }
- return true;
- }
-
- @NonNull
- private VorbisCommentHeader readCommentHeader(InputStream input) throws IOException, VorbisCommentReaderException {
- try {
- long vendorLength = EndianUtils.readSwappedUnsignedInteger(input);
- String vendorName = readUtf8String(input, vendorLength);
- long userCommentLength = EndianUtils.readSwappedUnsignedInteger(input);
- return new VorbisCommentHeader(vendorName, userCommentLength);
- } catch (UnsupportedEncodingException e) {
- throw new VorbisCommentReaderException(e);
- }
- }
-
- private String readContentVectorKey(InputStream input, long vectorLength) throws IOException {
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < vectorLength; i++) {
- char c = (char) input.read();
- if (c == '=') {
- return builder.toString();
- } else {
- builder.append(c);
- }
- }
- return null; // no key found
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReaderException.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReaderException.java
deleted file mode 100644
index 8b0f3052b..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReaderException.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package de.danoeh.antennapod.core.util.vorbiscommentreader;
-
-public class VorbisCommentReaderException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public VorbisCommentReaderException() {
- super();
- }
-
- public VorbisCommentReaderException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public VorbisCommentReaderException(String message) {
- super(message);
- }
-
- public VorbisCommentReaderException(Throwable message) {
- super(message);
- }
-}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/ChapterReaderTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/ChapterReaderTest.java
deleted file mode 100644
index fe3a3c05b..000000000
--- a/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/ChapterReaderTest.java
+++ /dev/null
@@ -1,209 +0,0 @@
-package de.danoeh.antennapod.core.util.id3reader;
-
-import de.danoeh.antennapod.model.feed.Chapter;
-import de.danoeh.antennapod.core.feed.ID3Chapter;
-import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
-import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
-import org.apache.commons.io.input.CountingInputStream;
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.util.List;
-
-import static de.danoeh.antennapod.core.util.id3reader.Id3ReaderTest.concat;
-import static de.danoeh.antennapod.core.util.id3reader.Id3ReaderTest.generateFrameHeader;
-import static de.danoeh.antennapod.core.util.id3reader.Id3ReaderTest.generateId3Header;
-import static org.junit.Assert.assertEquals;
-
-public class ChapterReaderTest {
- private static final byte CHAPTER_WITHOUT_SUBFRAME_START_TIME = 23;
- private static final byte[] CHAPTER_WITHOUT_SUBFRAME = {
- 'C', 'H', '1', 0, // String ID for mapping to CTOC
- 0, 0, 0, CHAPTER_WITHOUT_SUBFRAME_START_TIME, // Start time
- 0, 0, 0, 0, // End time
- 0, 0, 0, 0, // Start offset
- 0, 0, 0, 0 // End offset
- };
-
- @Test
- public void testReadFullTagWithChapter() throws IOException, ID3ReaderException {
- byte[] chapter = concat(
- generateFrameHeader(ChapterReader.FRAME_ID_CHAPTER, CHAPTER_WITHOUT_SUBFRAME.length),
- CHAPTER_WITHOUT_SUBFRAME);
- byte[] data = concat(
- generateId3Header(chapter.length),
- chapter);
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- ChapterReader reader = new ChapterReader(inputStream);
- reader.readInputStream();
- assertEquals(1, reader.getChapters().size());
- assertEquals(CHAPTER_WITHOUT_SUBFRAME_START_TIME, reader.getChapters().get(0).getStart());
- }
-
- @Test
- public void testReadFullTagWithMultipleChapters() throws IOException, ID3ReaderException {
- byte[] chapter = concat(
- generateFrameHeader(ChapterReader.FRAME_ID_CHAPTER, CHAPTER_WITHOUT_SUBFRAME.length),
- CHAPTER_WITHOUT_SUBFRAME);
- byte[] data = concat(
- generateId3Header(2 * chapter.length),
- chapter,
- chapter);
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- ChapterReader reader = new ChapterReader(inputStream);
- reader.readInputStream();
- assertEquals(2, reader.getChapters().size());
- assertEquals(CHAPTER_WITHOUT_SUBFRAME_START_TIME, reader.getChapters().get(0).getStart());
- assertEquals(CHAPTER_WITHOUT_SUBFRAME_START_TIME, reader.getChapters().get(1).getStart());
- }
-
- @Test
- public void testReadChapterWithoutSubframes() throws IOException, ID3ReaderException {
- FrameHeader header = new FrameHeader(ChapterReader.FRAME_ID_CHAPTER,
- CHAPTER_WITHOUT_SUBFRAME.length, (short) 0);
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(CHAPTER_WITHOUT_SUBFRAME));
- Chapter chapter = new ChapterReader(inputStream).readChapter(header);
- assertEquals(CHAPTER_WITHOUT_SUBFRAME_START_TIME, chapter.getStart());
- }
-
- @Test
- public void testReadChapterWithTitle() throws IOException, ID3ReaderException {
- byte[] title = {
- ID3Reader.ENCODING_ISO,
- 'H', 'e', 'l', 'l', 'o', // Title
- 0 // Null-terminated
- };
- byte[] chapterData = concat(
- CHAPTER_WITHOUT_SUBFRAME,
- generateFrameHeader(ChapterReader.FRAME_ID_TITLE, title.length),
- title);
- FrameHeader header = new FrameHeader(ChapterReader.FRAME_ID_CHAPTER, chapterData.length, (short) 0);
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(chapterData));
- ChapterReader reader = new ChapterReader(inputStream);
- Chapter chapter = reader.readChapter(header);
- assertEquals(CHAPTER_WITHOUT_SUBFRAME_START_TIME, chapter.getStart());
- assertEquals("Hello", chapter.getTitle());
- }
-
- @Test
- public void testReadTitleWithGarbage() throws IOException, ID3ReaderException {
- byte[] titleSubframeContent = {
- ID3Reader.ENCODING_ISO,
- 'A', // Title
- 0, // Null-terminated
- 42, 42, 42, 42 // Garbage, should be ignored
- };
- FrameHeader header = new FrameHeader(ChapterReader.FRAME_ID_TITLE, titleSubframeContent.length, (short) 0);
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(titleSubframeContent));
- ChapterReader reader = new ChapterReader(inputStream);
- Chapter chapter = new ID3Chapter("", 0);
- reader.readChapterSubFrame(header, chapter);
- assertEquals("A", chapter.getTitle());
-
- // Should skip the garbage and point to the next frame
- assertEquals(titleSubframeContent.length, reader.getPosition());
- }
-
- @Test
- public void testRealFileUltraschall() throws IOException, ID3ReaderException {
- CountingInputStream inputStream = new CountingInputStream(getClass().getClassLoader()
- .getResource("media-parser/ultraschall5.mp3").openStream());
- ChapterReader reader = new ChapterReader(inputStream);
- reader.readInputStream();
- List<Chapter> chapters = reader.getChapters();
-
- assertEquals(3, chapters.size());
-
- assertEquals(0, chapters.get(0).getStart());
- assertEquals(4004, chapters.get(1).getStart());
- assertEquals(7999, chapters.get(2).getStart());
-
- assertEquals("Marke 1", chapters.get(0).getTitle());
- assertEquals("Marke 2", chapters.get(1).getTitle());
- assertEquals("Marke 3", chapters.get(2).getTitle());
-
- assertEquals("https://example.com", chapters.get(0).getLink());
- assertEquals("https://example.com", chapters.get(1).getLink());
- assertEquals("https://example.com", chapters.get(2).getLink());
-
- assertEquals(EmbeddedChapterImage.makeUrl(16073, 2750569), chapters.get(0).getImageUrl());
- assertEquals(EmbeddedChapterImage.makeUrl(2766765, 15740), chapters.get(1).getImageUrl());
- assertEquals(EmbeddedChapterImage.makeUrl(2782628, 2750569), chapters.get(2).getImageUrl());
- }
-
- @Test
- public void testRealFileAuphonic() throws IOException, ID3ReaderException {
- CountingInputStream inputStream = new CountingInputStream(getClass().getClassLoader()
- .getResource("media-parser/auphonic.mp3").openStream());
- ChapterReader reader = new ChapterReader(inputStream);
- reader.readInputStream();
- List<Chapter> chapters = reader.getChapters();
-
- assertEquals(4, chapters.size());
-
- assertEquals(0, chapters.get(0).getStart());
- assertEquals(3000, chapters.get(1).getStart());
- assertEquals(6000, chapters.get(2).getStart());
- assertEquals(9000, chapters.get(3).getStart());
-
- assertEquals("Chapter 1 - ❤️😊", chapters.get(0).getTitle());
- assertEquals("Chapter 2 - ßöÄ", chapters.get(1).getTitle());
- assertEquals("Chapter 3 - 爱", chapters.get(2).getTitle());
- assertEquals("Chapter 4", chapters.get(3).getTitle());
-
- assertEquals("https://example.com", chapters.get(0).getLink());
- assertEquals("https://example.com", chapters.get(1).getLink());
- assertEquals("https://example.com", chapters.get(2).getLink());
- assertEquals("https://example.com", chapters.get(3).getLink());
-
- assertEquals(EmbeddedChapterImage.makeUrl(765, 308), chapters.get(0).getImageUrl());
- assertEquals(EmbeddedChapterImage.makeUrl(1271, 308), chapters.get(1).getImageUrl());
- assertEquals(EmbeddedChapterImage.makeUrl(1771, 308), chapters.get(2).getImageUrl());
- assertEquals(EmbeddedChapterImage.makeUrl(2259, 308), chapters.get(3).getImageUrl());
- }
-
- @Test
- public void testRealFileHindenburgJournalistPro() throws IOException, ID3ReaderException {
- CountingInputStream inputStream = new CountingInputStream(getClass().getClassLoader()
- .getResource("media-parser/hindenburg-journalist-pro.mp3").openStream());
- ChapterReader reader = new ChapterReader(inputStream);
- reader.readInputStream();
- List<Chapter> chapters = reader.getChapters();
-
- assertEquals(2, chapters.size());
-
- assertEquals(0, chapters.get(0).getStart());
- assertEquals(5006, chapters.get(1).getStart());
-
- assertEquals("Chapter Marker 1", chapters.get(0).getTitle());
- assertEquals("Chapter Marker 2", chapters.get(1).getTitle());
-
- assertEquals("https://example.com/chapter1url", chapters.get(0).getLink());
- assertEquals("https://example.com/chapter2url", chapters.get(1).getLink());
-
- assertEquals(EmbeddedChapterImage.makeUrl(5330, 4015), chapters.get(0).getImageUrl());
- assertEquals(EmbeddedChapterImage.makeUrl(9498, 4364), chapters.get(1).getImageUrl());
- }
-
- @Test
- public void testRealFileMp3chapsPy() throws IOException, ID3ReaderException {
- CountingInputStream inputStream = new CountingInputStream(getClass().getClassLoader()
- .getResource("media-parser/mp3chaps-py.mp3").openStream());
- ChapterReader reader = new ChapterReader(inputStream);
- reader.readInputStream();
- List<Chapter> chapters = reader.getChapters();
-
- assertEquals(4, chapters.size());
-
- assertEquals(0, chapters.get(0).getStart());
- assertEquals(7000, chapters.get(1).getStart());
- assertEquals(9000, chapters.get(2).getStart());
- assertEquals(11000, chapters.get(3).getStart());
-
- assertEquals("Start", chapters.get(0).getTitle());
- assertEquals("Chapter 1", chapters.get(1).getTitle());
- assertEquals("Chapter 2", chapters.get(2).getTitle());
- assertEquals("Chapter 3", chapters.get(3).getTitle());
- }
-}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/Id3ReaderTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/Id3ReaderTest.java
deleted file mode 100644
index 584141b83..000000000
--- a/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/Id3ReaderTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package de.danoeh.antennapod.core.util.id3reader;
-
-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;
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-public class Id3ReaderTest {
- @Test
- public void testReadString() throws IOException {
- byte[] data = {
- ID3Reader.ENCODING_ISO,
- 'T', 'e', 's', 't',
- 0 // Null-terminated
- };
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- String string = new ID3Reader(inputStream).readEncodingAndString(1000);
- assertEquals("Test", string);
- }
-
- @Test
- public void testReadMultipleStrings() throws IOException {
- byte[] data = {
- ID3Reader.ENCODING_ISO,
- 'F', 'o', 'o',
- 0, // Null-terminated
- ID3Reader.ENCODING_ISO,
- 'B', 'a', 'r',
- 0 // Null-terminated
- };
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- ID3Reader reader = new ID3Reader(inputStream);
- assertEquals("Foo", reader.readEncodingAndString(1000));
- assertEquals("Bar", reader.readEncodingAndString(1000));
- }
-
- @Test
- public void testReadingLimit() throws IOException {
- byte[] data = {
- ID3Reader.ENCODING_ISO,
- 'A', 'B', 'C', 'D'
- };
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- ID3Reader reader = new ID3Reader(inputStream);
- assertEquals("ABC", reader.readEncodingAndString(4)); // Includes encoding
- assertEquals('D', reader.readByte());
- }
-
- @Test
- public void testReadUtf16RespectsBom() throws IOException {
- byte[] data = {
- ID3Reader.ENCODING_UTF16_WITH_BOM,
- (byte) 0xff, (byte) 0xfe, // BOM: Little-endian
- 'A', 0, 'B', 0, 'C', 0,
- 0, 0, // Null-terminated
- ID3Reader.ENCODING_UTF16_WITH_BOM,
- (byte) 0xfe, (byte) 0xff, // BOM: Big-endian
- 0, 'D', 0, 'E', 0, 'F',
- 0, 0, // Null-terminated
- };
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- ID3Reader reader = new ID3Reader(inputStream);
- assertEquals("ABC", reader.readEncodingAndString(1000));
- assertEquals("DEF", reader.readEncodingAndString(1000));
- }
-
- @Test
- public void testReadUtf16NullPrefix() throws IOException {
- byte[] data = {
- ID3Reader.ENCODING_UTF16_WITH_BOM,
- (byte) 0xff, (byte) 0xfe, // BOM
- 0x00, 0x01, // Latin Capital Letter A with macron (Ā)
- 0, 0, // Null-terminated
- };
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- String string = new ID3Reader(inputStream).readEncodingAndString(1000);
- assertEquals("Ā", string);
- }
-
- @Test
- public void testReadingLimitUtf16() throws IOException {
- byte[] data = {
- ID3Reader.ENCODING_UTF16_WITHOUT_BOM,
- 'A', 0, 'B', 0, 'C', 0, 'D', 0
- };
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- ID3Reader reader = new ID3Reader(inputStream);
- reader.readEncodingAndString(6); // Includes encoding, produces broken string
- assertTrue("Should respect limit even if it breaks a symbol", reader.getPosition() <= 6);
- }
-
- @Test
- public void testReadTagHeader() throws IOException, ID3ReaderException {
- byte[] data = generateId3Header(23);
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- TagHeader header = new ID3Reader(inputStream).readTagHeader();
- assertEquals("ID3", header.getId());
- assertEquals(42, header.getVersion());
- assertEquals(23, header.getSize());
- }
-
- @Test
- public void testReadFrameHeader() throws IOException {
- byte[] data = generateFrameHeader("CHAP", 42);
- CountingInputStream inputStream = new CountingInputStream(new ByteArrayInputStream(data));
- FrameHeader header = new ID3Reader(inputStream).readFrameHeader();
- assertEquals("CHAP", header.getId());
- assertEquals(42, header.getSize());
- }
-
- public static byte[] generateFrameHeader(String id, int size) {
- return concat(
- id.getBytes(StandardCharsets.ISO_8859_1), // Frame ID
- new byte[] {
- (byte) (size >> 24), (byte) (size >> 16),
- (byte) (size >> 8), (byte) (size), // Size
- 0, 0 // Flags
- });
- }
-
- static byte[] generateId3Header(int size) {
- return new byte[] {
- 'I', 'D', '3', // Identifier
- 0, 42, // Version
- 0, // Flags
- (byte) (size >> 24), (byte) (size >> 16),
- (byte) (size >> 8), (byte) (size), // Size
- };
- }
-
- static byte[] concat(byte[]... arrays) {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- try {
- for (byte[] array : arrays) {
- outputStream.write(array);
- }
- } catch (IOException e) {
- fail(e.getMessage());
- }
- return outputStream.toByteArray();
- }
-}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReaderTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReaderTest.java
deleted file mode 100644
index 40f8b381f..000000000
--- a/core/src/test/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReaderTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package de.danoeh.antennapod.core.util.vorbiscommentreader;
-
-import de.danoeh.antennapod.model.feed.Chapter;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-
-import static org.junit.Assert.assertEquals;
-
-public class VorbisCommentChapterReaderTest {
-
- @Test
- public void testRealFilesAuphonic() throws IOException, VorbisCommentReaderException {
- testRealFileAuphonic("media-parser/auphonic.ogg");
- testRealFileAuphonic("media-parser/auphonic.opus");
- }
-
- public void testRealFileAuphonic(String filename) throws IOException, VorbisCommentReaderException {
- InputStream inputStream = getClass().getClassLoader()
- .getResource(filename).openStream();
- VorbisCommentChapterReader reader = new VorbisCommentChapterReader();
- reader.readInputStream(inputStream);
- List<Chapter> chapters = reader.getChapters();
-
- assertEquals(4, chapters.size());
-
- assertEquals(0, chapters.get(0).getStart());
- assertEquals(3000, chapters.get(1).getStart());
- assertEquals(6000, chapters.get(2).getStart());
- assertEquals(9000, chapters.get(3).getStart());
-
- assertEquals("Chapter 1 - ❤️😊", chapters.get(0).getTitle());
- assertEquals("Chapter 2 - ßöÄ", chapters.get(1).getTitle());
- assertEquals("Chapter 3 - 爱", chapters.get(2).getTitle());
- assertEquals("Chapter 4", chapters.get(3).getTitle());
-
- assertEquals("https://example.com", chapters.get(0).getLink());
- assertEquals("https://example.com", chapters.get(1).getLink());
- assertEquals("https://example.com", chapters.get(2).getLink());
- assertEquals("https://example.com", chapters.get(3).getLink());
- }
-}
diff --git a/core/src/test/resources/media-parser/auphonic.m4a b/core/src/test/resources/media-parser/auphonic.m4a
deleted file mode 100644
index ca59a80f6..000000000
--- a/core/src/test/resources/media-parser/auphonic.m4a
+++ /dev/null
Binary files differ
diff --git a/core/src/test/resources/media-parser/auphonic.mp3 b/core/src/test/resources/media-parser/auphonic.mp3
deleted file mode 100644
index ca2a7ed4f..000000000
--- a/core/src/test/resources/media-parser/auphonic.mp3
+++ /dev/null
Binary files differ
diff --git a/core/src/test/resources/media-parser/auphonic.ogg b/core/src/test/resources/media-parser/auphonic.ogg
deleted file mode 100644
index de326517a..000000000
--- a/core/src/test/resources/media-parser/auphonic.ogg
+++ /dev/null
Binary files differ
diff --git a/core/src/test/resources/media-parser/auphonic.opus b/core/src/test/resources/media-parser/auphonic.opus
deleted file mode 100644
index 08538ecb7..000000000
--- a/core/src/test/resources/media-parser/auphonic.opus
+++ /dev/null
Binary files differ
diff --git a/core/src/test/resources/media-parser/hindenburg-journalist-pro.m4a b/core/src/test/resources/media-parser/hindenburg-journalist-pro.m4a
deleted file mode 100644
index bd64dd9da..000000000
--- a/core/src/test/resources/media-parser/hindenburg-journalist-pro.m4a
+++ /dev/null
Binary files differ
diff --git a/core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3 b/core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3
deleted file mode 100644
index d341b6045..000000000
--- a/core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3
+++ /dev/null
Binary files differ
diff --git a/core/src/test/resources/media-parser/mp3chaps-py.mp3 b/core/src/test/resources/media-parser/mp3chaps-py.mp3
deleted file mode 100644
index 05d519fb0..000000000
--- a/core/src/test/resources/media-parser/mp3chaps-py.mp3
+++ /dev/null
Binary files differ
diff --git a/core/src/test/resources/media-parser/ultraschall5.mp3 b/core/src/test/resources/media-parser/ultraschall5.mp3
deleted file mode 100644
index a73029a54..000000000
--- a/core/src/test/resources/media-parser/ultraschall5.mp3
+++ /dev/null
Binary files differ