diff options
11 files changed, 187 insertions, 96 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java index d4d338d7b..3dfe661d1 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -135,10 +135,10 @@ public class OnlineFeedViewActivity extends AppCompatActivity { String feedUrl = null; if (getIntent().hasExtra(ARG_FEEDURL)) { feedUrl = getIntent().getStringExtra(ARG_FEEDURL); - } else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND) - || TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { - feedUrl = TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND) - ? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString(); + } else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)) { + feedUrl = getIntent().getStringExtra(Intent.EXTRA_TEXT); + } else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { + feedUrl = getIntent().getDataString(); } if (feedUrl == null) { diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java index 8f1cfb374..e46073457 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java @@ -17,12 +17,14 @@ import de.danoeh.antennapod.core.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadRequestCreator; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; +import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.model.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.model.download.DownloadError; import de.danoeh.antennapod.core.util.DownloadErrorLabel; import de.danoeh.antennapod.model.feed.Feed; +import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.ui.common.ThemeUtils; import de.danoeh.antennapod.view.viewholder.DownloadLogItemViewHolder; @@ -157,8 +159,15 @@ public class DownloadLogAdapter extends BaseAdapter { holder.secondaryActionButton.setContentDescription(context.getString(R.string.cancel_download_label)); holder.secondaryActionButton.setVisibility(View.VISIBLE); holder.secondaryActionButton.setTag(downloader); - holder.secondaryActionButton.setOnClickListener(v -> - listFragment.onListItemClick(null, holder.itemView, position, 0)); + holder.secondaryActionButton.setOnClickListener(v -> { + DownloadService.cancel(context, request.getSource()); + if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { + FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId()); + FeedItem feedItem = media.getItem(); + feedItem.disableAutoDownload(); + DBWriter.setFeedItem(feedItem); + } + }); holder.reason.setVisibility(View.GONE); holder.tapForDetails.setVisibility(View.GONE); holder.icon.setTextColor(ThemeUtils.getColorFromAttr(context, R.attr.colorPrimary)); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java index 804e8dde3..9f0995f38 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java @@ -15,7 +15,6 @@ import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.DownloadLogEvent; import de.danoeh.antennapod.core.event.DownloaderUpdate; import de.danoeh.antennapod.core.menuhandler.MenuItemUtils; -import de.danoeh.antennapod.core.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.model.download.DownloadStatus; import de.danoeh.antennapod.core.service.download.Downloader; @@ -23,8 +22,6 @@ import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.dialog.DownloadLogDetailsDialog; -import de.danoeh.antennapod.model.feed.FeedItem; -import de.danoeh.antennapod.model.feed.FeedMedia; import de.danoeh.antennapod.ui.common.PagedToolbarFragment; import de.danoeh.antennapod.view.EmptyViewHandler; import io.reactivex.Observable; @@ -98,17 +95,7 @@ public class DownloadLogFragment extends ListFragment { super.onListItemClick(l, v, position, id); Object item = adapter.getItem(position); - if (item instanceof Downloader) { - DownloadRequest downloadRequest = ((Downloader) item).getDownloadRequest(); - DownloadService.cancel(getContext(), downloadRequest.getSource()); - - if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { - FeedMedia media = DBReader.getFeedMedia(downloadRequest.getFeedfileId()); - FeedItem feedItem = media.getItem(); - feedItem.disableAutoDownload(); - DBWriter.setFeedItem(feedItem); - } - } else if (item instanceof DownloadStatus) { + if (item instanceof DownloadStatus) { new DownloadLogDetailsDialog(getContext(), (DownloadStatus) item).show(); } } 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()); + } +} |