diff options
author | ByteHamster <info@bytehamster.com> | 2021-08-28 09:52:45 +0200 |
---|---|---|
committer | ByteHamster <info@bytehamster.com> | 2021-08-28 10:59:26 +0200 |
commit | ca64739f363e585758b6ada6cc4e6c9d43191724 (patch) | |
tree | 3b5b6634792a3997d9302053e628ec8cda205ff5 /core | |
parent | ddae5e2278fe6b6e950576cdc460ec7ffe761d5d (diff) | |
download | AntennaPod-ca64739f363e585758b6ada6cc4e6c9d43191724.zip |
Moved media file parser to its own module
Diffstat (limited to 'core')
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 Binary files differdeleted file mode 100644 index ca59a80f6..000000000 --- a/core/src/test/resources/media-parser/auphonic.m4a +++ /dev/null diff --git a/core/src/test/resources/media-parser/auphonic.mp3 b/core/src/test/resources/media-parser/auphonic.mp3 Binary files differdeleted file mode 100644 index ca2a7ed4f..000000000 --- a/core/src/test/resources/media-parser/auphonic.mp3 +++ /dev/null diff --git a/core/src/test/resources/media-parser/auphonic.ogg b/core/src/test/resources/media-parser/auphonic.ogg Binary files differdeleted file mode 100644 index de326517a..000000000 --- a/core/src/test/resources/media-parser/auphonic.ogg +++ /dev/null diff --git a/core/src/test/resources/media-parser/auphonic.opus b/core/src/test/resources/media-parser/auphonic.opus Binary files differdeleted file mode 100644 index 08538ecb7..000000000 --- a/core/src/test/resources/media-parser/auphonic.opus +++ /dev/null diff --git a/core/src/test/resources/media-parser/hindenburg-journalist-pro.m4a b/core/src/test/resources/media-parser/hindenburg-journalist-pro.m4a Binary files differdeleted file mode 100644 index bd64dd9da..000000000 --- a/core/src/test/resources/media-parser/hindenburg-journalist-pro.m4a +++ /dev/null diff --git a/core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3 b/core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3 Binary files differdeleted file mode 100644 index d341b6045..000000000 --- a/core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3 +++ /dev/null diff --git a/core/src/test/resources/media-parser/mp3chaps-py.mp3 b/core/src/test/resources/media-parser/mp3chaps-py.mp3 Binary files differdeleted file mode 100644 index 05d519fb0..000000000 --- a/core/src/test/resources/media-parser/mp3chaps-py.mp3 +++ /dev/null diff --git a/core/src/test/resources/media-parser/ultraschall5.mp3 b/core/src/test/resources/media-parser/ultraschall5.mp3 Binary files differdeleted file mode 100644 index a73029a54..000000000 --- a/core/src/test/resources/media-parser/ultraschall5.mp3 +++ /dev/null |