content: package allen.town.podcast.core.glide;
  
  import androidx.annotation.NonNull;
  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 allen.town.podcast.core.service.download.PodcastHttpClient;
  import allen.town.podcast.model.feed.EmbeddedChapterImage;
  import java.io.BufferedInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.nio.ByteBuffer;
  import okhttp3.Request;
  import okhttp3.Response;
  import org.apache.commons.io.IOUtils;
  
  public final class ChapterImageModelLoader implements ModelLoader<EmbeddedChapterImage, ByteBuffer> {
  
      public static class Factory implements ModelLoaderFactory<EmbeddedChapterImage, ByteBuffer> {
          @NonNull
          @Override
          public ModelLoader<EmbeddedChapterImage, ByteBuffer> build(@NonNull MultiModelLoaderFactory unused) {
              return new ChapterImageModelLoader();
          }
  
          @Override
          public void teardown() {
              // Do nothing.
          }
      }
  
      @Override
      public LoadData<ByteBuffer> buildLoadData(@NonNull EmbeddedChapterImage model,
                                                int width,
                                                int height,
                                                @NonNull Options options) {
          return new LoadData<>(new ObjectKey(model), new EmbeddedImageFetcher(model));
      }
  
      @Override
      public boolean handles(@NonNull EmbeddedChapterImage model) {
          return true;
      }
  
      static 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));
                      IOUtils.skip(stream, image.getPosition());
                      byte[] imageContent = new byte[image.getLength()];
                      IOUtils.read(stream, imageContent, 0, image.getLength());
                      callback.onDataReady(ByteBuffer.wrap(imageContent));
                  } else {
                      Request.Builder httpReq = new Request.Builder();
                      // Skipping would download the whole file
                      httpReq.header("Range", "bytes=" + image.getPosition()
                              + "-" + (image.getPosition() + image.getLength()));
                      httpReq.url(image.getMedia().getStreamUrl());
                      Response response = PodcastHttpClient.getHttpClient().newCall(httpReq.build()).execute();
                      if (!response.isSuccessful() || response.body() == null) {
                          throw new IOException("Invalid response: " + response.code() + " " + response.message());
                      }
                      callback.onDataReady(ByteBuffer.wrap(response.body().bytes()));
                  }
              } catch (IOException e) {
                  callback.onLoadFailed(e);
              } 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;
          }
      }
  }
  