summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2022-03-11 19:58:41 +0100
committerByteHamster <info@bytehamster.com>2022-03-11 20:57:24 +0100
commit933fde839e9d5a1c538de6ed4f740f9ac285da5a (patch)
treec52338f284901db9fea8fbd411a0d42d81ce4028
parent36a36e4f853259f34fc3608ed1b21d575129e274 (diff)
downloadAntennaPod-933fde839e9d5a1c538de6ed4f740f9ac285da5a.zip
Read vorbis description of local files
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java4
-rw-r--r--parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReader.java34
-rw-r--r--parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReader.java32
-rw-r--r--parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java84
-rw-r--r--parser/media/src/test/java/de/danoeh/antennapod/parser/media/id3/MetadataReaderTest.java50
-rw-r--r--parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReaderTest.java4
-rw-r--r--parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReaderTest.java28
8 files changed, 171 insertions, 76 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
index a63b9d41c..988b7c015 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
@@ -38,6 +38,8 @@ import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.parser.feed.util.MimeTypeUtils;
import de.danoeh.antennapod.parser.media.id3.ID3ReaderException;
import de.danoeh.antennapod.parser.media.id3.Id3MetadataReader;
+import de.danoeh.antennapod.parser.media.vorbis.VorbisCommentMetadataReader;
+import de.danoeh.antennapod.parser.media.vorbis.VorbisCommentReaderException;
import org.apache.commons.io.input.CountingInputStream;
public class LocalFeedUpdater {
@@ -209,8 +211,15 @@ public class LocalFeedUpdater {
reader.readInputStream();
item.setDescriptionIfLonger(reader.getComment());
} catch (IOException | ID3ReaderException e) {
- // Do not flood Logcat with full stack traces
Log.d(TAG, "Unable to parse ID3 of " + file.getUri() + ": " + e.getMessage());
+
+ try (InputStream inputStream = context.getContentResolver().openInputStream(file.getUri())) {
+ VorbisCommentMetadataReader reader = new VorbisCommentMetadataReader(inputStream);
+ reader.readInputStream();
+ item.setDescriptionIfLonger(reader.getDescription());
+ } catch (IOException | VorbisCommentReaderException e2) {
+ Log.d(TAG, "Unable to parse vorbis comments of " + file.getUri() + ": " + e2.getMessage());
+ }
}
}
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 e9f812c7f..723ea1d47 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
@@ -171,8 +171,8 @@ public class ChapterUtils {
@NonNull
private static List<Chapter> readOggChaptersFromInputStream(InputStream input) throws VorbisCommentReaderException {
- VorbisCommentChapterReader reader = new VorbisCommentChapterReader();
- reader.readInputStream(input);
+ VorbisCommentChapterReader reader = new VorbisCommentChapterReader(input);
+ reader.readInputStream();
List<Chapter> chapters = reader.getChapters();
if (chapters == null) {
return Collections.emptyList();
diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReader.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReader.java
index e3b91a0e7..8290a547a 100644
--- a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReader.java
+++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReader.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.parser.media.vorbis;
import android.util.Log;
+import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -17,25 +18,15 @@ public class VorbisCommentChapterReader extends VorbisCommentReader {
private static final String CHAPTER_ATTRIBUTE_LINK = "url";
private static final int CHAPTERXXX_LENGTH = "chapterxxx".length();
- private List<Chapter> chapters;
+ private final List<Chapter> chapters = new ArrayList<>();
- public VorbisCommentChapterReader() {
+ public VorbisCommentChapterReader(InputStream input) {
+ super(input);
}
@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);
+ public boolean handles(String key) {
+ return key.matches(CHAPTER_KEY);
}
@Override
@@ -68,19 +59,6 @@ public class VorbisCommentChapterReader extends VorbisCommentReader {
}
}
- @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 (("" + id).equals(c.getChapterId())) {
diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReader.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReader.java
new file mode 100644
index 000000000..158a0d8f7
--- /dev/null
+++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReader.java
@@ -0,0 +1,32 @@
+package de.danoeh.antennapod.parser.media.vorbis;
+
+import java.io.InputStream;
+
+public class VorbisCommentMetadataReader extends VorbisCommentReader {
+ private static final String KEY_DESCRIPTION = "description";
+ private static final String KEY_COMMENT = "comment";
+
+ private String description = null;
+
+ public VorbisCommentMetadataReader(InputStream input) {
+ super(input);
+ }
+
+ @Override
+ public boolean handles(String key) {
+ return KEY_DESCRIPTION.equals(key) || KEY_COMMENT.equals(key);
+ }
+
+ @Override
+ public void onContentVectorValue(String key, String value) {
+ if (KEY_DESCRIPTION.equals(key) || KEY_COMMENT.equals(key)) {
+ if (description == null || value.length() > description.length()) {
+ description = value;
+ }
+ }
+ }
+
+ public String getDescription() {
+ return description;
+ }
+}
diff --git a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java
index 3d5f29f17..13126a73d 100644
--- a/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java
+++ b/parser/media/src/main/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentReader.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.parser.media.vorbis;
import androidx.annotation.NonNull;
import org.apache.commons.io.EndianUtils;
import org.apache.commons.io.IOUtils;
+import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
@@ -12,51 +13,35 @@ import java.nio.charset.Charset;
import java.util.Locale;
public abstract class VorbisCommentReader {
- /** Length of first page in an ogg file in bytes. */
+ private static final String TAG = "VorbisCommentReader";
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();
+ private final InputStream input;
- 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 onEndOfComment();
-
- protected abstract void onError(VorbisCommentReaderException exception);
+ VorbisCommentReader(InputStream input) {
+ this.input = input;
+ }
- public void readInputStream(InputStream input) throws VorbisCommentReaderException {
+ public void readInputStream() throws VorbisCommentReaderException {
try {
- findIdentificationHeader(input);
- onVorbisCommentFound();
- findOggPage(input);
- findCommentHeader(input);
- VorbisCommentHeader commentHeader = readCommentHeader(input);
- onVorbisCommentHeaderFound(commentHeader);
+ findIdentificationHeader();
+ findOggPage();
+ findCommentHeader();
+ VorbisCommentHeader commentHeader = readCommentHeader();
+ Log.d(TAG, commentHeader.toString());
for (int i = 0; i < commentHeader.getUserCommentLength(); i++) {
- readUserComment(input);
+ readUserComment();
}
- onEndOfComment();
} catch (IOException e) {
- onError(new VorbisCommentReaderException(e));
+ e.printStackTrace();
}
}
- private void findOggPage(InputStream input) throws IOException {
+ private void findOggPage() throws IOException {
// find OggS
byte[] buffer = new byte[4];
final byte[] oggPageHeader = {'O', 'g', 'g', 'S'};
@@ -76,17 +61,19 @@ public abstract class VorbisCommentReader {
IOUtils.skipFully(input, numSegments);
}
- private void readUserComment(InputStream input) throws VorbisCommentReaderException {
+ private void readUserComment() throws VorbisCommentReaderException {
try {
long vectorLength = EndianUtils.readSwappedUnsignedInteger(input);
if (vectorLength > 20 * 1024 * 1024) {
- // Avoid reading entire file if it is encoded incorrectly
- throw new VorbisCommentReaderException("User comment unrealistically long: " + vectorLength);
+ String keyPart = readUtf8String(10);
+ throw new VorbisCommentReaderException("User comment unrealistically long. "
+ + "key=" + keyPart + ", length=" + vectorLength);
}
- String key = readContentVectorKey(input, vectorLength).toLowerCase(Locale.US);
- boolean readValue = onContentVectorKey(key);
- if (readValue) {
- String value = readUtf8String(input, vectorLength - key.length() - 1);
+ String key = readContentVectorKey(vectorLength).toLowerCase(Locale.US);
+ boolean shouldReadValue = handles(key);
+ Log.d(TAG, "key=" + key + ", length=" + vectorLength + ", handles=" + shouldReadValue);
+ if (shouldReadValue) {
+ String value = readUtf8String(vectorLength - key.length() - 1);
onContentVectorValue(key, value);
} else {
IOUtils.skipFully(input, vectorLength - key.length() - 1);
@@ -96,7 +83,7 @@ public abstract class VorbisCommentReader {
}
}
- private String readUtf8String(InputStream input, long length) throws IOException {
+ private String readUtf8String(long length) throws IOException {
byte[] buffer = new byte[(int) length];
IOUtils.readFully(input, buffer);
Charset charset = Charset.forName("UTF-8");
@@ -107,7 +94,7 @@ public abstract class VorbisCommentReader {
* Looks for an identification header in the first page of the file. If an
* identification header is found, it will be skipped completely
*/
- private void findIdentificationHeader(InputStream input) throws IOException {
+ private void findIdentificationHeader() 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' };
@@ -122,7 +109,7 @@ public abstract class VorbisCommentReader {
throw new IOException("No vorbis identification header found");
}
- private void findCommentHeader(InputStream input) throws IOException {
+ private void findCommentHeader() 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++) {
@@ -155,10 +142,10 @@ public abstract class VorbisCommentReader {
}
@NonNull
- private VorbisCommentHeader readCommentHeader(InputStream input) throws IOException, VorbisCommentReaderException {
+ private VorbisCommentHeader readCommentHeader() throws IOException, VorbisCommentReaderException {
try {
long vendorLength = EndianUtils.readSwappedUnsignedInteger(input);
- String vendorName = readUtf8String(input, vendorLength);
+ String vendorName = readUtf8String(vendorLength);
long userCommentLength = EndianUtils.readSwappedUnsignedInteger(input);
return new VorbisCommentHeader(vendorName, userCommentLength);
} catch (UnsupportedEncodingException e) {
@@ -166,7 +153,7 @@ public abstract class VorbisCommentReader {
}
}
- private String readContentVectorKey(InputStream input, long vectorLength) throws IOException {
+ private String readContentVectorKey(long vectorLength) throws IOException {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < vectorLength; i++) {
char c = (char) input.read();
@@ -178,4 +165,15 @@ public abstract class VorbisCommentReader {
}
return null; // no key found
}
+
+ /**
+ * 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 handles(String key);
+
+ /**
+ * Is called if onContentVectorKey returned true for the key.
+ */
+ protected abstract void onContentVectorValue(String key, String value) throws VorbisCommentReaderException;
}
diff --git a/parser/media/src/test/java/de/danoeh/antennapod/parser/media/id3/MetadataReaderTest.java b/parser/media/src/test/java/de/danoeh/antennapod/parser/media/id3/MetadataReaderTest.java
new file mode 100644
index 000000000..5b6b06ea7
--- /dev/null
+++ b/parser/media/src/test/java/de/danoeh/antennapod/parser/media/id3/MetadataReaderTest.java
@@ -0,0 +1,50 @@
+package de.danoeh.antennapod.parser.media.id3;
+
+import org.apache.commons.io.input.CountingInputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(RobolectricTestRunner.class)
+public class MetadataReaderTest {
+ @Test
+ public void testRealFileUltraschall() throws IOException, ID3ReaderException {
+ CountingInputStream inputStream = new CountingInputStream(getClass().getClassLoader()
+ .getResource("ultraschall5.mp3").openStream());
+ Id3MetadataReader reader = new Id3MetadataReader(inputStream);
+ reader.readInputStream();
+ assertEquals("Description", reader.getComment());
+ }
+
+ @Test
+ public void testRealFileAuphonic() throws IOException, ID3ReaderException {
+ CountingInputStream inputStream = new CountingInputStream(getClass().getClassLoader()
+ .getResource("auphonic.mp3").openStream());
+ Id3MetadataReader reader = new Id3MetadataReader(inputStream);
+ reader.readInputStream();
+ assertEquals("Summary", reader.getComment());
+ }
+
+ @Test
+ public void testRealFileHindenburgJournalistPro() throws IOException, ID3ReaderException {
+ CountingInputStream inputStream = new CountingInputStream(getClass().getClassLoader()
+ .getResource("hindenburg-journalist-pro.mp3").openStream());
+ Id3MetadataReader reader = new Id3MetadataReader(inputStream);
+ reader.readInputStream();
+ assertEquals("This is the summary of this podcast episode. This file was made with"
+ + " Hindenburg Journalist Pro version 1.85, build number 2360.", reader.getComment());
+ }
+
+ @Test
+ public void testRealFileMp3chapsPy() throws IOException, ID3ReaderException {
+ CountingInputStream inputStream = new CountingInputStream(getClass().getClassLoader()
+ .getResource("mp3chaps-py.mp3").openStream());
+ Id3MetadataReader reader = new Id3MetadataReader(inputStream);
+ reader.readInputStream();
+ assertEquals("2021.08.13", reader.getComment());
+ }
+}
diff --git a/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReaderTest.java b/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReaderTest.java
index 6ebe875d9..b19be324d 100644
--- a/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReaderTest.java
+++ b/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentChapterReaderTest.java
@@ -23,8 +23,8 @@ public class VorbisCommentChapterReaderTest {
public void testRealFileAuphonic(String filename) throws IOException, VorbisCommentReaderException {
InputStream inputStream = getClass().getClassLoader()
.getResource(filename).openStream();
- VorbisCommentChapterReader reader = new VorbisCommentChapterReader();
- reader.readInputStream(inputStream);
+ VorbisCommentChapterReader reader = new VorbisCommentChapterReader(inputStream);
+ reader.readInputStream();
List<Chapter> chapters = reader.getChapters();
assertEquals(4, chapters.size());
diff --git a/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReaderTest.java b/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReaderTest.java
new file mode 100644
index 000000000..7f71c8fa9
--- /dev/null
+++ b/parser/media/src/test/java/de/danoeh/antennapod/parser/media/vorbis/VorbisCommentMetadataReaderTest.java
@@ -0,0 +1,28 @@
+package de.danoeh.antennapod.parser.media.vorbis;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(RobolectricTestRunner.class)
+public class VorbisCommentMetadataReaderTest {
+
+ @Test
+ public void testRealFilesAuphonic() throws IOException, VorbisCommentReaderException {
+ testRealFileAuphonic("auphonic.ogg");
+ testRealFileAuphonic("auphonic.opus");
+ }
+
+ public void testRealFileAuphonic(String filename) throws IOException, VorbisCommentReaderException {
+ InputStream inputStream = getClass().getClassLoader()
+ .getResource(filename).openStream();
+ VorbisCommentMetadataReader reader = new VorbisCommentMetadataReader(inputStream);
+ reader.readInputStream();
+ assertEquals("Summary", reader.getDescription());
+ }
+}