Auto Download: Exponential backoff when file 404s

This commit is contained in:
Martin Fietz 2016-01-14 19:52:54 +01:00
parent 4f864ab2de
commit f6d5c780e5
4 changed files with 62 additions and 14 deletions

View File

@ -11,6 +11,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.asynctask.ImageResource; import de.danoeh.antennapod.core.asynctask.ImageResource;
import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBReader;
@ -75,7 +76,13 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
private List<Chapter> chapters; private List<Chapter> chapters;
private FeedImage image; private FeedImage image;
private boolean autoDownload = true; /*
* 0: auto download disabled
* 1: auto download enabled
* > 1: auto download enabled, (approx.) timestamp of the last failed attempt
* where last digit denotes the number of failed attempts
*/
private long autoDownload = 0;
/** /**
* Any tags assigned to this item * Any tags assigned to this item
@ -93,7 +100,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
* */ * */
public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId, public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId,
FlattrStatus flattrStatus, boolean hasChapters, FeedImage image, int state, FlattrStatus flattrStatus, boolean hasChapters, FeedImage image, int state,
String itemIdentifier, boolean autoDownload) { String itemIdentifier, long autoDownload) {
this.id = id; this.id = id;
this.title = title; this.title = title;
this.link = link; this.link = link;
@ -162,7 +169,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
FlattrStatus flattrStatus = new FlattrStatus(cursor.getLong(indexFlattrStatus)); FlattrStatus flattrStatus = new FlattrStatus(cursor.getLong(indexFlattrStatus));
int state = cursor.getInt(indexRead); int state = cursor.getInt(indexRead);
String itemIdentifier = cursor.getString(indexItemIdentifier); String itemIdentifier = cursor.getString(indexItemIdentifier);
boolean autoDownload = cursor.getInt(indexAutoDownload) > 0; long autoDownload = cursor.getLong(indexAutoDownload);
FeedItem item = new FeedItem(id, title, link, pubDate, paymentLink, feedId, flattrStatus, FeedItem item = new FeedItem(id, title, link, pubDate, paymentLink, feedId, flattrStatus,
hasChapters, null, state, itemIdentifier, autoDownload); hasChapters, null, state, itemIdentifier, autoDownload);
@ -449,18 +456,37 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
} }
public void setAutoDownload(boolean autoDownload) { public void setAutoDownload(boolean autoDownload) {
this.autoDownload = autoDownload; this.autoDownload = autoDownload ? 1 : 0;
} }
public boolean getAutoDownload() { public boolean getAutoDownload() {
return this.autoDownload; return this.autoDownload > 0;
}
public int getFailedAutoDownloadAttempts() {
if (autoDownload <= 1) {
return 0;
}
int failedAttempts = (int)(autoDownload % 10);
if (failedAttempts == 0) {
failedAttempts = 10;
}
return failedAttempts;
} }
public boolean isAutoDownloadable() { public boolean isAutoDownloadable() {
return this.hasMedia() && if (media == null || media.isPlaying() || media.isDownloaded() || autoDownload == 0) {
false == this.getMedia().isPlaying() && return false;
false == this.getMedia().isDownloaded() && }
this.getAutoDownload(); if (autoDownload == 1) {
return true;
}
int failedAttempts = getFailedAutoDownloadAttempts();
double magicValue = 1.767; // 1.767^(10[=#maxNumAttempts]-1) = 168 hours / 7 days
int millisecondsInHour = 3600000;
long waitingTime = (long) (Math.pow(magicValue, failedAttempts - 1) * millisecondsInHour);
long grace = TimeUnit.MINUTES.toMillis(5);
return System.currentTimeMillis() > (autoDownload + waitingTime - grace);
} }
/** /**

View File

@ -206,8 +206,7 @@ public class DownloadService extends Service {
FeedItem item = media.getItem(); FeedItem item = media.getItem();
if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR && if (status.getReason() == DownloadError.ERROR_HTTP_DATA_ERROR &&
Integer.valueOf(status.getReasonDetailed()) == HttpURLConnection.HTTP_NOT_FOUND) { Integer.valueOf(status.getReasonDetailed()) == HttpURLConnection.HTTP_NOT_FOUND) {
item.setAutoDownload(false); // for event bus DBWriter.saveFeedItemAutoDownloadFailed(item).get();
DBWriter.setFeedItemAutoDownload(item, false);
} }
// to make lists reload the failed item, we fake an item update // to make lists reload the failed item, we fake an item update
EventBus.getDefault().post(FeedItemEvent.updated(item)); EventBus.getDefault().post(FeedItemEvent.updated(item));

View File

@ -977,11 +977,33 @@ public class DBWriter {
* Sets the 'auto_download'-attribute of specific FeedItem. * Sets the 'auto_download'-attribute of specific FeedItem.
* *
* @param feedItem FeedItem. * @param feedItem FeedItem.
* @param autoDownload true enables auto download, false disables it
*/ */
public static Future<?> setFeedItemAutoDownload(final FeedItem feedItem, public static Future<?> setFeedItemAutoDownload(final FeedItem feedItem,
final boolean autoDownload) { final boolean autoDownload) {
Log.d(TAG, "FeedItem[id=" + feedItem.getId() + "] SET auto_download " + autoDownload);
return dbExec.submit(() -> { return dbExec.submit(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItemAutoDownload(feedItem, autoDownload ? 1 : 0);
adapter.close();
EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
});
}
public static Future<?> saveFeedItemAutoDownloadFailed(final FeedItem feedItem) {
return dbExec.submit(() -> {
int failedAttempts = feedItem.getFailedAutoDownloadAttempts() + 1;
Log.d(TAG, "failedAttempts: " + failedAttempts);
long autoDownload;
if(!feedItem.getAutoDownload() || failedAttempts >= 10) {
autoDownload = 0; // giving up, disable auto download
feedItem.setAutoDownload(false);
} else {
long now = System.currentTimeMillis();
autoDownload = (now / 10) * 10 + failedAttempts;
Log.d(TAG, "now: " + now);
Log.d(TAG, "autoDownload: " + autoDownload);
}
final PodDBAdapter adapter = PodDBAdapter.getInstance(); final PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open(); adapter.open();
adapter.setFeedItemAutoDownload(feedItem, autoDownload); adapter.setFeedItemAutoDownload(feedItem, autoDownload);
@ -992,7 +1014,8 @@ public class DBWriter {
/** /**
* Sets the 'auto_download'-attribute of specific FeedItem. * Sets the 'auto_download'-attribute of specific FeedItem.
* @param feed This feed's episodes will be processed. *
* @param feed This feed's episodes will be processed.
* @param autoDownload If true, auto download will be enabled for the feed's episodes. Else, * @param autoDownload If true, auto download will be enabled for the feed's episodes. Else,
*/ */
public static Future<?> setFeedsItemsAutoDownload(final Feed feed, public static Future<?> setFeedsItemsAutoDownload(final Feed feed,

View File

@ -794,7 +794,7 @@ public class PodDBAdapter {
return status.getId(); return status.getId();
} }
public void setFeedItemAutoDownload(FeedItem feedItem, boolean autoDownload) { public void setFeedItemAutoDownload(FeedItem feedItem, long autoDownload) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(KEY_AUTO_DOWNLOAD, autoDownload); values.put(KEY_AUTO_DOWNLOAD, autoDownload);
db.update(TABLE_NAME_FEED_ITEMS, values, KEY_ID + "=?", db.update(TABLE_NAME_FEED_ITEMS, values, KEY_ID + "=?",