summaryrefslogtreecommitdiff
path: root/core/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ResizingOkHttpStreamFetcher.java132
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/URLChecker.java24
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java4
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);
}