From 8dc8cc64a8a6de3adfba1aaf0a0306f90727a2c4 Mon Sep 17 00:00:00 2001 From: Tony Tam <149837+tonytamsf@users.noreply.github.com> Date: Sun, 17 Mar 2024 00:52:16 -1000 Subject: [PATCH] Allow retrying chapter loading if interrupted (#6828) Chapter loading can sometimes get interrupted, most importantly if the corresponding fragment tries to refresh the view again. Before, this set the chapters to an empty list, indicating that it should not be tried again. Now, interrupted exceptions do not set the list to be empty, so it can be retried later. --- .../handler/MediaDownloadedHandler.java | 17 +++-- .../antennapod/core/util/ChapterUtils.java | 66 +++++++++++-------- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java index 98c470f21..55831b821 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java @@ -10,6 +10,7 @@ import de.danoeh.antennapod.model.MediaMetadataRetrieverCompat; import org.greenrobot.eventbus.EventBus; import java.io.File; +import java.io.InterruptedIOException; import java.util.concurrent.ExecutionException; import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; @@ -54,14 +55,18 @@ public class MediaDownloadedHandler implements Runnable { media.setSize(new File(request.getDestination()).length()); media.checkEmbeddedPicture(); // enforce check - // check if file has chapters - if (media.getItem() != null && !media.getItem().hasChapters()) { - media.setChapters(ChapterUtils.loadChaptersFromMediaFile(media, context)); + try { + // Cache chapters if file has them + if (media.getItem() != null && !media.getItem().hasChapters()) { + media.setChapters(ChapterUtils.loadChaptersFromMediaFile(media, context)); + } + if (media.getItem() != null && media.getItem().getPodcastIndexChapterUrl() != null) { + ChapterUtils.loadChaptersFromUrl(media.getItem().getPodcastIndexChapterUrl(), false); + } + } catch (InterruptedIOException ignore) { + // Ignore } - if (media.getItem() != null && media.getItem().getPodcastIndexChapterUrl() != null) { - ChapterUtils.loadChaptersFromUrl(media.getItem().getPodcastIndexChapterUrl(), false); - } // Get duration String durationStr = null; try (MediaMetadataRetrieverCompat mmr = new MediaMetadataRetrieverCompat()) { 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 084b34e76..901771913 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 @@ -28,6 +28,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InterruptedIOException; import java.util.Collections; import java.util.List; @@ -60,42 +61,50 @@ public class ChapterUtils { return; } - List chaptersFromDatabase = null; - List chaptersFromPodcastIndex = null; - if (playable instanceof FeedMedia) { - FeedMedia feedMedia = (FeedMedia) playable; - if (feedMedia.getItem() == null) { - feedMedia.setItem(DBReader.getFeedItem(feedMedia.getItemId())); - } - if (feedMedia.getItem().hasChapters()) { - chaptersFromDatabase = DBReader.loadChaptersOfFeedItem(feedMedia.getItem()); + try { + List chaptersFromDatabase = null; + List chaptersFromPodcastIndex = null; + if (playable instanceof FeedMedia) { + FeedMedia feedMedia = (FeedMedia) playable; + if (feedMedia.getItem() == null) { + feedMedia.setItem(DBReader.getFeedItem(feedMedia.getItemId())); + } + if (feedMedia.getItem().hasChapters()) { + chaptersFromDatabase = DBReader.loadChaptersOfFeedItem(feedMedia.getItem()); + } + + if (!TextUtils.isEmpty(feedMedia.getItem().getPodcastIndexChapterUrl())) { + chaptersFromPodcastIndex = ChapterUtils.loadChaptersFromUrl( + feedMedia.getItem().getPodcastIndexChapterUrl(), forceRefresh); + } + } - if (!TextUtils.isEmpty(feedMedia.getItem().getPodcastIndexChapterUrl())) { - chaptersFromPodcastIndex = ChapterUtils.loadChaptersFromUrl( - feedMedia.getItem().getPodcastIndexChapterUrl(), forceRefresh); + List chaptersFromMediaFile = ChapterUtils.loadChaptersFromMediaFile(playable, context); + List chaptersMergePhase1 = ChapterMerger.merge(chaptersFromDatabase, chaptersFromMediaFile); + List chapters = ChapterMerger.merge(chaptersMergePhase1, chaptersFromPodcastIndex); + if (chapters == null) { + // Do not try loading again. There are no chapters or parsing failed. + playable.setChapters(Collections.emptyList()); + } else { + playable.setChapters(chapters); } - - } - - List chaptersFromMediaFile = ChapterUtils.loadChaptersFromMediaFile(playable, context); - List chaptersMergePhase1 = ChapterMerger.merge(chaptersFromDatabase, chaptersFromMediaFile); - List chapters = ChapterMerger.merge(chaptersMergePhase1, chaptersFromPodcastIndex); - if (chapters == null) { - // Do not try loading again. There are no chapters. - playable.setChapters(Collections.emptyList()); - } else { - playable.setChapters(chapters); + } catch (InterruptedIOException e) { + Log.d(TAG, "Chapter loading interrupted"); + playable.setChapters(null); // Allow later retry } } - public static List loadChaptersFromMediaFile(Playable playable, Context context) { + public static List loadChaptersFromMediaFile(Playable playable, Context context) + throws InterruptedIOException { try (CountingInputStream in = openStream(playable, context)) { List chapters = readId3ChaptersFrom(in); if (!chapters.isEmpty()) { Log.i(TAG, "Chapters loaded"); return chapters; } + } catch (InterruptedIOException e) { + throw e; } catch (IOException | ID3ReaderException e) { Log.e(TAG, "Unable to load ID3 chapters: " + e.getMessage()); } @@ -106,6 +115,8 @@ public class ChapterUtils { Log.i(TAG, "Chapters loaded"); return chapters; } + } catch (InterruptedIOException e) { + throw e; } catch (IOException | VorbisCommentReaderException e) { Log.e(TAG, "Unable to load vorbis chapters: " + e.getMessage()); } @@ -135,7 +146,7 @@ public class ChapterUtils { } } - public static List loadChaptersFromUrl(String url, boolean forceRefresh) { + public static List loadChaptersFromUrl(String url, boolean forceRefresh) throws InterruptedIOException { if (forceRefresh) { return loadChaptersFromUrl(url, CacheControl.FORCE_NETWORK); } @@ -147,7 +158,8 @@ public class ChapterUtils { return cachedChapters; } - private static List loadChaptersFromUrl(String url, CacheControl cacheControl) { + private static List loadChaptersFromUrl(String url, CacheControl cacheControl) + throws InterruptedIOException { Response response = null; try { Request request = new Request.Builder().url(url).cacheControl(cacheControl).build(); @@ -155,6 +167,8 @@ public class ChapterUtils { if (response.isSuccessful() && response.body() != null) { return PodcastIndexChapterParser.parse(response.body().string()); } + } catch (InterruptedIOException e) { + throw e; } catch (IOException e) { e.printStackTrace(); } finally {