diff options
Diffstat (limited to 'core/src')
5 files changed, 169 insertions, 16 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java index b2c809e90..50511526f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java @@ -11,10 +11,12 @@ import com.bumptech.glide.load.DecodeFormat; import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory; import com.bumptech.glide.module.AppGlideModule; +import de.danoeh.antennapod.core.util.EmbeddedChapterImage; import java.io.InputStream; import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.core.preferences.UserPreferences; +import java.nio.ByteBuffer; /** * {@see com.bumptech.glide.integration.okhttp.OkHttpGlideModule} @@ -32,5 +34,6 @@ public class ApGlideModule extends AppGlideModule { @Override public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { registry.replace(String.class, InputStream.class, new ApOkHttpUrlLoader.Factory()); + registry.append(EmbeddedChapterImage.class, ByteBuffer.class, new ChapterImageModelLoader.Factory()); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java new file mode 100644 index 000000000..6548e9c5e --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java @@ -0,0 +1,98 @@ +package de.danoeh.antennapod.core.glide; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.data.DataFetcher; +import com.bumptech.glide.load.model.ModelLoader; +import com.bumptech.glide.load.model.ModelLoaderFactory; +import com.bumptech.glide.load.model.MultiModelLoaderFactory; +import com.bumptech.glide.signature.ObjectKey; +import de.danoeh.antennapod.core.feed.Chapter; +import de.danoeh.antennapod.core.util.EmbeddedChapterImage; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.ByteBuffer; +import org.apache.commons.io.IOUtils; + +public final class ChapterImageModelLoader implements ModelLoader<EmbeddedChapterImage, ByteBuffer> { + + public static class Factory implements ModelLoaderFactory<EmbeddedChapterImage, ByteBuffer> { + @Override + public ModelLoader<EmbeddedChapterImage, ByteBuffer> build(MultiModelLoaderFactory unused) { + return new ChapterImageModelLoader(); + } + + @Override + public void teardown() { + // Do nothing. + } + } + + @Nullable + @Override + public LoadData<ByteBuffer> buildLoadData(EmbeddedChapterImage model, int width, int height, Options options) { + return new LoadData<>(new ObjectKey(model), new EmbeddedImageFetcher(model)); + } + + @Override + public boolean handles(EmbeddedChapterImage model) { + return true; + } + + class EmbeddedImageFetcher implements DataFetcher<ByteBuffer> { + private final EmbeddedChapterImage image; + + public EmbeddedImageFetcher(EmbeddedChapterImage image) { + this.image = image; + } + + @Override + public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) { + + BufferedInputStream stream = null; + try { + if (image.getMedia().localFileAvailable()) { + File localFile = new File(image.getMedia().getLocalMediaUrl()); + stream = new BufferedInputStream(new FileInputStream(localFile)); + } else { + URL url = new URL(image.getMedia().getStreamUrl()); + stream = new BufferedInputStream(url.openStream()); + } + byte[] imageContent = new byte[image.getLength()]; + stream.skip(image.getPosition()); + stream.read(imageContent, 0, image.getLength()); + callback.onDataReady(ByteBuffer.wrap(imageContent)); + } catch (IOException e) { + callback.onLoadFailed(new IOException("Loading embedded cover did not work")); + e.printStackTrace(); + } finally { + IOUtils.closeQuietly(stream); + } + } + + @Override public void cleanup() { + // nothing to clean up + } + @Override public void cancel() { + // cannot cancel + } + + @NonNull + @Override + public Class<ByteBuffer> getDataClass() { + return ByteBuffer.class; + } + + @NonNull + @Override + public DataSource getDataSource() { + return DataSource.LOCAL; + } + } +} 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 e69bb2863..b75887154 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 @@ -36,24 +36,17 @@ public class ChapterUtils { private ChapterUtils() { } - @Nullable - public static Chapter getCurrentChapter(Playable media) { - if (media.getChapters() == null) { - return null; + public static int getCurrentChapterIndex(Playable media, int position) { + if (media == null || media.getChapters() == null || media.getChapters().size() == 0) { + return -1; } List<Chapter> chapters = media.getChapters(); - if (chapters == null) { - return null; - } - Chapter current = chapters.get(0); - for (Chapter sc : chapters) { - if (sc.getStart() > media.getPosition()) { - break; - } else { - current = sc; + for (int i = 0; i < chapters.size(); i++) { + if (chapters.get(i).getStart() > position) { + return i - 1; } } - return current; + return chapters.size() - 1; } public static void loadChaptersFromStreamUrl(Playable media) { diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java b/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java new file mode 100644 index 000000000..5cb62e6c2 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java @@ -0,0 +1,58 @@ +package de.danoeh.antennapod.core.util; + +import de.danoeh.antennapod.core.util.playback.Playable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class EmbeddedChapterImage { + private static final Pattern EMBEDDED_IMAGE_MATCHER = Pattern.compile("embedded-image://(.*)@(\\d+):(\\d+)"); + final String mime; + final int position; + final int length; + final Playable media; + + public EmbeddedChapterImage(Playable media, String imageUrl) { + this.media = media; + Matcher m = EMBEDDED_IMAGE_MATCHER.matcher(imageUrl); + if (m.find()) { + this.mime = m.group(1); + this.position = Integer.parseInt(m.group(2)); + this.length = Integer.parseInt(m.group(3)); + } else { + throw new IllegalArgumentException("Not an embedded chapter"); + } + } + + public static String makeUrl(String mime, int position, int length) { + return "embedded-image://" + mime + "@" + position + ":" + length; + } + + public String getMime() { + return mime; + } + + public int getPosition() { + return position; + } + + public int getLength() { + return length; + } + + public Playable getMedia() { + return media; + } + + private static boolean isEmbeddedChapterImage(String imageUrl) { + return EMBEDDED_IMAGE_MATCHER.matcher(imageUrl).matches(); + } + + public static Object getModelFor(Playable media, int chapter) { + String imageUrl = media.getChapters().get(chapter).getImageUrl(); + if (isEmbeddedChapterImage(imageUrl)) { + return new EmbeddedChapterImage(media, imageUrl); + } else { + return imageUrl; + } + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java index b007967c1..934e0b00c 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java @@ -4,6 +4,7 @@ import android.text.TextUtils; import android.util.Log; import de.danoeh.antennapod.core.feed.Chapter; import de.danoeh.antennapod.core.feed.ID3Chapter; +import de.danoeh.antennapod.core.util.EmbeddedChapterImage; import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader; import de.danoeh.antennapod.core.util.id3reader.model.TagHeader; @@ -104,8 +105,8 @@ public class ChapterReader extends ID3Reader { // Data contains the picture int length = header.getSize() - read; if (TextUtils.isEmpty(currentChapter.getImageUrl()) || type == IMAGE_TYPE_COVER) { - currentChapter.setImageUrl("embedded-image://" + mime.toString() - + "@" + input.getByteCount() + ":" + length); + currentChapter.setImageUrl( + EmbeddedChapterImage.makeUrl(mime.toString(), input.getCount(), length)); } skipBytes(input, length); } |