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.
This commit is contained in:
Tony Tam 2024-03-17 00:52:16 -10:00 committed by GitHub
parent 48c0ccb4a2
commit 8dc8cc64a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 51 additions and 32 deletions

View File

@ -10,6 +10,7 @@ import de.danoeh.antennapod.model.MediaMetadataRetrieverCompat;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import java.io.File; import java.io.File;
import java.io.InterruptedIOException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
@ -54,14 +55,18 @@ public class MediaDownloadedHandler implements Runnable {
media.setSize(new File(request.getDestination()).length()); media.setSize(new File(request.getDestination()).length());
media.checkEmbeddedPicture(); // enforce check media.checkEmbeddedPicture(); // enforce check
// check if file has chapters try {
if (media.getItem() != null && !media.getItem().hasChapters()) { // Cache chapters if file has them
media.setChapters(ChapterUtils.loadChaptersFromMediaFile(media, context)); 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 // Get duration
String durationStr = null; String durationStr = null;
try (MediaMetadataRetrieverCompat mmr = new MediaMetadataRetrieverCompat()) { try (MediaMetadataRetrieverCompat mmr = new MediaMetadataRetrieverCompat()) {

View File

@ -28,6 +28,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InterruptedIOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -60,42 +61,50 @@ public class ChapterUtils {
return; return;
} }
List<Chapter> chaptersFromDatabase = null; try {
List<Chapter> chaptersFromPodcastIndex = null; List<Chapter> chaptersFromDatabase = null;
if (playable instanceof FeedMedia) { List<Chapter> chaptersFromPodcastIndex = null;
FeedMedia feedMedia = (FeedMedia) playable; if (playable instanceof FeedMedia) {
if (feedMedia.getItem() == null) { FeedMedia feedMedia = (FeedMedia) playable;
feedMedia.setItem(DBReader.getFeedItem(feedMedia.getItemId())); if (feedMedia.getItem() == null) {
} feedMedia.setItem(DBReader.getFeedItem(feedMedia.getItemId()));
if (feedMedia.getItem().hasChapters()) { }
chaptersFromDatabase = DBReader.loadChaptersOfFeedItem(feedMedia.getItem()); 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())) { List<Chapter> chaptersFromMediaFile = ChapterUtils.loadChaptersFromMediaFile(playable, context);
chaptersFromPodcastIndex = ChapterUtils.loadChaptersFromUrl( List<Chapter> chaptersMergePhase1 = ChapterMerger.merge(chaptersFromDatabase, chaptersFromMediaFile);
feedMedia.getItem().getPodcastIndexChapterUrl(), forceRefresh); List<Chapter> 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);
} }
} catch (InterruptedIOException e) {
} Log.d(TAG, "Chapter loading interrupted");
playable.setChapters(null); // Allow later retry
List<Chapter> chaptersFromMediaFile = ChapterUtils.loadChaptersFromMediaFile(playable, context);
List<Chapter> chaptersMergePhase1 = ChapterMerger.merge(chaptersFromDatabase, chaptersFromMediaFile);
List<Chapter> 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);
} }
} }
public static List<Chapter> loadChaptersFromMediaFile(Playable playable, Context context) { public static List<Chapter> loadChaptersFromMediaFile(Playable playable, Context context)
throws InterruptedIOException {
try (CountingInputStream in = openStream(playable, context)) { try (CountingInputStream in = openStream(playable, context)) {
List<Chapter> chapters = readId3ChaptersFrom(in); List<Chapter> chapters = readId3ChaptersFrom(in);
if (!chapters.isEmpty()) { if (!chapters.isEmpty()) {
Log.i(TAG, "Chapters loaded"); Log.i(TAG, "Chapters loaded");
return chapters; return chapters;
} }
} catch (InterruptedIOException e) {
throw e;
} catch (IOException | ID3ReaderException e) { } catch (IOException | ID3ReaderException e) {
Log.e(TAG, "Unable to load ID3 chapters: " + e.getMessage()); Log.e(TAG, "Unable to load ID3 chapters: " + e.getMessage());
} }
@ -106,6 +115,8 @@ public class ChapterUtils {
Log.i(TAG, "Chapters loaded"); Log.i(TAG, "Chapters loaded");
return chapters; return chapters;
} }
} catch (InterruptedIOException e) {
throw e;
} catch (IOException | VorbisCommentReaderException e) { } catch (IOException | VorbisCommentReaderException e) {
Log.e(TAG, "Unable to load vorbis chapters: " + e.getMessage()); Log.e(TAG, "Unable to load vorbis chapters: " + e.getMessage());
} }
@ -135,7 +146,7 @@ public class ChapterUtils {
} }
} }
public static List<Chapter> loadChaptersFromUrl(String url, boolean forceRefresh) { public static List<Chapter> loadChaptersFromUrl(String url, boolean forceRefresh) throws InterruptedIOException {
if (forceRefresh) { if (forceRefresh) {
return loadChaptersFromUrl(url, CacheControl.FORCE_NETWORK); return loadChaptersFromUrl(url, CacheControl.FORCE_NETWORK);
} }
@ -147,7 +158,8 @@ public class ChapterUtils {
return cachedChapters; return cachedChapters;
} }
private static List<Chapter> loadChaptersFromUrl(String url, CacheControl cacheControl) { private static List<Chapter> loadChaptersFromUrl(String url, CacheControl cacheControl)
throws InterruptedIOException {
Response response = null; Response response = null;
try { try {
Request request = new Request.Builder().url(url).cacheControl(cacheControl).build(); Request request = new Request.Builder().url(url).cacheControl(cacheControl).build();
@ -155,6 +167,8 @@ public class ChapterUtils {
if (response.isSuccessful() && response.body() != null) { if (response.isSuccessful() && response.body() != null) {
return PodcastIndexChapterParser.parse(response.body().string()); return PodcastIndexChapterParser.parse(response.body().string());
} }
} catch (InterruptedIOException e) {
throw e;
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} finally { } finally {