diff options
Diffstat (limited to 'core/src/main')
5 files changed, 159 insertions, 12 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java index 8c80e9151..a0ec16578 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java @@ -4,7 +4,6 @@ import android.content.ContentResolver; import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher; import com.bumptech.glide.load.Options; import com.bumptech.glide.load.model.GlideUrl; import com.bumptech.glide.load.model.ModelLoader; @@ -77,7 +76,7 @@ class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> { @Nullable @Override public LoadData<InputStream> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) { - return new LoadData<>(new ObjectKey(model), new OkHttpStreamFetcher(client, new GlideUrl(model))); + return new LoadData<>(new ObjectKey(model), new ResizingOkHttpStreamFetcher(client, new GlideUrl(model))); } @Override diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ResizingOkHttpStreamFetcher.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ResizingOkHttpStreamFetcher.java new file mode 100644 index 000000000..7b8eed6e0 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ResizingOkHttpStreamFetcher.java @@ -0,0 +1,132 @@ +package de.danoeh.antennapod.core.glide; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Build; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.bumptech.glide.Priority; +import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher; +import com.bumptech.glide.load.model.GlideUrl; +import com.google.android.exoplayer2.util.Log; +import okhttp3.Call; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +public class ResizingOkHttpStreamFetcher extends OkHttpStreamFetcher { + private static final String TAG = "ResizingOkHttpStreamFetcher"; + private static final int MAX_DIMENSIONS = 2000; + private static final int MAX_FILE_SIZE = 1024 * 1024; // 1 MB + + private FileInputStream stream; + private File tempIn; + private File tempOut; + + public ResizingOkHttpStreamFetcher(Call.Factory client, GlideUrl url) { + super(client, url); + } + + @Override + public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { + super.loadData(priority, new DataCallback<InputStream>() { + @Override + public void onDataReady(@Nullable InputStream data) { + if (data == null) { + callback.onDataReady(null); + return; + } + try { + tempIn = File.createTempFile("resize_", null); + tempOut = File.createTempFile("resize_", null); + OutputStream outputStream = new FileOutputStream(tempIn); + IOUtils.copy(data, outputStream); + outputStream.close(); + IOUtils.closeQuietly(data); + + if (tempIn.length() <= MAX_FILE_SIZE) { + try { + stream = new FileInputStream(tempIn); + callback.onDataReady(stream); // Just deliver the original, non-scaled image + } catch (FileNotFoundException fileNotFoundException) { + callback.onLoadFailed(fileNotFoundException); + } + return; + } + + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + FileInputStream in = new FileInputStream(tempIn); + BitmapFactory.decodeStream(in, null, options); + IOUtils.closeQuietly(in); + + if (Math.max(options.outHeight, options.outWidth) >= MAX_DIMENSIONS) { + double sampleSize = (double) Math.max(options.outHeight, options.outWidth) / MAX_DIMENSIONS; + options.inSampleSize = (int) Math.pow(2d, Math.floor(Math.log(sampleSize) / Math.log(2d))); + } + + options.inJustDecodeBounds = false; + in = new FileInputStream(tempIn); + Bitmap bitmap = BitmapFactory.decodeStream(in, null, options); + IOUtils.closeQuietly(in); + + Bitmap.CompressFormat format = Build.VERSION.SDK_INT < 30 + ? Bitmap.CompressFormat.WEBP : Bitmap.CompressFormat.WEBP_LOSSY; + + int quality = 100; + while (true) { + FileOutputStream out = new FileOutputStream(tempOut); + bitmap.compress(format, quality, out); + IOUtils.closeQuietly(out); + + if (tempOut.length() > 3 * MAX_FILE_SIZE && quality >= 45) { + quality -= 40; + } else if (tempOut.length() > 2 * MAX_FILE_SIZE && quality >= 25) { + quality -= 20; + } else if (tempOut.length() > MAX_FILE_SIZE && quality >= 15) { + quality -= 10; + } else if (tempOut.length() > MAX_FILE_SIZE && quality >= 10) { + quality -= 5; + } else { + break; + } + } + + stream = new FileInputStream(tempOut); + callback.onDataReady(stream); + Log.d(TAG, "Compressed image from " + tempIn.length() / 1024 + + " to " + tempOut.length() / 1024 + " kB (quality: " + quality + "%)"); + } catch (Exception e) { + e.printStackTrace(); + + try { + stream = new FileInputStream(tempIn); + callback.onDataReady(stream); // Just deliver the original, non-scaled image + } catch (FileNotFoundException fileNotFoundException) { + e.printStackTrace(); + callback.onLoadFailed(fileNotFoundException); + } + } + } + + @Override + public void onLoadFailed(@NonNull Exception e) { + callback.onLoadFailed(e); + } + }); + } + + @Override + public void cleanup() { + IOUtils.closeQuietly(stream); + FileUtils.deleteQuietly(tempIn); + FileUtils.deleteQuietly(tempOut); + super.cleanup(); + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java index 348ffaa60..3dba0735d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java @@ -47,8 +47,8 @@ public class NSMedia extends Namespace { String medium = attributes.getValue(MEDIUM); boolean validTypeMedia = false; boolean validTypeImage = false; - boolean isDefault = "true".equals(defaultStr); + String guessedType = SyndTypeUtils.getMimeTypeFromUrl(url); if (MEDIUM_AUDIO.equals(medium)) { validTypeMedia = true; @@ -56,12 +56,14 @@ public class NSMedia extends Namespace { } else if (MEDIUM_VIDEO.equals(medium)) { validTypeMedia = true; type = "video/*"; - } else if (MEDIUM_IMAGE.equals(medium)) { + } else if (MEDIUM_IMAGE.equals(medium) && (guessedType == null + || (!guessedType.startsWith("audio/") && !guessedType.startsWith("video/")))) { + // Apparently, some publishers explicitly specify the audio file as an image validTypeImage = true; type = "image/*"; } else { if (type == null) { - type = SyndTypeUtils.getMimeTypeFromUrl(url); + type = guessedType; } if (SyndTypeUtils.enclosureTypeValid(type)) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java index cb7db1709..1ac068307 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java @@ -5,9 +5,10 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import android.util.Log; -import de.danoeh.antennapod.core.BuildConfig; import okhttp3.HttpUrl; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; @@ -28,6 +29,7 @@ public final class URLChecker { private static final String TAG = "URLChecker"; private static final String AP_SUBSCRIBE = "antennapod-subscribe://"; + private static final String AP_SUBSCRIBE_DEEPLINK = "antennapod.org/deeplink/subscribe?url="; /** * Checks if URL is valid and modifies it if necessary. @@ -39,22 +41,30 @@ public final class URLChecker { url = url.trim(); String lowerCaseUrl = url.toLowerCase(); // protocol names are case insensitive if (lowerCaseUrl.startsWith("feed://")) { - if (BuildConfig.DEBUG) Log.d(TAG, "Replacing feed:// with http://"); + Log.d(TAG, "Replacing feed:// with http://"); return prepareURL(url.substring("feed://".length())); } else if (lowerCaseUrl.startsWith("pcast://")) { - if (BuildConfig.DEBUG) Log.d(TAG, "Removing pcast://"); + Log.d(TAG, "Removing pcast://"); return prepareURL(url.substring("pcast://".length())); } else if (lowerCaseUrl.startsWith("pcast:")) { - if (BuildConfig.DEBUG) Log.d(TAG, "Removing pcast:"); + Log.d(TAG, "Removing pcast:"); return prepareURL(url.substring("pcast:".length())); } else if (lowerCaseUrl.startsWith("itpc")) { - if (BuildConfig.DEBUG) Log.d(TAG, "Replacing itpc:// with http://"); + Log.d(TAG, "Replacing itpc:// with http://"); return prepareURL(url.substring("itpc://".length())); } else if (lowerCaseUrl.startsWith(AP_SUBSCRIBE)) { - if (BuildConfig.DEBUG) Log.d(TAG, "Removing antennapod-subscribe://"); + Log.d(TAG, "Removing antennapod-subscribe://"); return prepareURL(url.substring(AP_SUBSCRIBE.length())); + } else if (lowerCaseUrl.contains(AP_SUBSCRIBE_DEEPLINK)) { + Log.d(TAG, "Removing " + AP_SUBSCRIBE_DEEPLINK); + String removedWebsite = url.substring(url.indexOf(AP_SUBSCRIBE_DEEPLINK) + AP_SUBSCRIBE_DEEPLINK.length()); + try { + return prepareURL(URLDecoder.decode(removedWebsite, "UTF-8")); + } catch (UnsupportedEncodingException e) { + return prepareURL(removedWebsite); + } } else if (!(lowerCaseUrl.startsWith("http://") || lowerCaseUrl.startsWith("https://"))) { - if (BuildConfig.DEBUG) Log.d(TAG, "Adding http:// at the beginning of the URL"); + Log.d(TAG, "Adding http:// at the beginning of the URL"); return "http://" + url; } else { return url; diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java index 17313ca14..b8ec3524b 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java @@ -96,6 +96,10 @@ public class ID3Reader { short version = readShort(); byte flags = readByte(); int size = unsynchsafe(readInt()); + if ((flags & 0b01000000) != 0) { + int extendedHeaderSize = readInt(); + skipBytes(extendedHeaderSize - 4); + } return new TagHeader("ID3", size, version, flags); } |