DownloadManager.java 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package eu.kanade.mangafeed.data.helpers;
  2. import android.content.Context;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.util.List;
  6. import eu.kanade.mangafeed.data.models.Chapter;
  7. import eu.kanade.mangafeed.data.models.Manga;
  8. import eu.kanade.mangafeed.data.models.Page;
  9. import eu.kanade.mangafeed.events.DownloadChapterEvent;
  10. import eu.kanade.mangafeed.sources.base.Source;
  11. import eu.kanade.mangafeed.util.DiskUtils;
  12. import rx.Observable;
  13. import rx.Subscription;
  14. import rx.schedulers.Schedulers;
  15. import rx.subjects.PublishSubject;
  16. public class DownloadManager {
  17. private PublishSubject<DownloadChapterEvent> downloadsSubject;
  18. private Subscription downloadsSubscription;
  19. private Context context;
  20. private SourceManager sourceManager;
  21. private PreferencesHelper preferences;
  22. public DownloadManager(Context context, SourceManager sourceManager, PreferencesHelper preferences) {
  23. this.context = context;
  24. this.sourceManager = sourceManager;
  25. this.preferences = preferences;
  26. initializeDownloadSubscription();
  27. }
  28. private void initializeDownloadSubscription() {
  29. if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed()) {
  30. downloadsSubscription.unsubscribe();
  31. }
  32. downloadsSubject = PublishSubject.create();
  33. downloadsSubscription = downloadsSubject
  34. .subscribeOn(Schedulers.io())
  35. .concatMap(event -> downloadChapter(event.getManga(), event.getChapter()))
  36. .onBackpressureBuffer()
  37. .subscribe();
  38. }
  39. private Observable<Page> downloadChapter(Manga manga, Chapter chapter) {
  40. final Source source = sourceManager.get(manga.source);
  41. final File chapterDirectory = new File(
  42. preferences.getDownloadsDirectory(), getChapterDirectory(source, manga, chapter));
  43. return source
  44. .pullPageListFromNetwork(chapter.url)
  45. // Ensure we don't download a chapter already downloaded
  46. .filter(pages -> !isChapterDownloaded(chapterDirectory, pages))
  47. // Get all the URLs to the source images, fetch pages if necessary
  48. .flatMap(pageList -> Observable.merge(
  49. Observable.from(pageList).filter(page -> page.getImageUrl() != null),
  50. source.getRemainingImageUrlsFromPageList(pageList)))
  51. // Start downloading images
  52. .flatMap(page -> getDownloadedImage(page, source, chapterDirectory));
  53. }
  54. private String getChapterDirectory(Source source, Manga manga, Chapter chapter) {
  55. return source.getName() +
  56. File.separator +
  57. manga.title.replaceAll("[^a-zA-Z0-9.-]", "_") +
  58. File.separator +
  59. chapter.name.replaceAll("[^a-zA-Z0-9.-]", "_");
  60. }
  61. private String getImageFilename(Page page) {
  62. return page.getImageUrl().substring(
  63. page.getImageUrl().lastIndexOf("/") + 1,
  64. page.getImageUrl().length());
  65. }
  66. private boolean isChapterDownloaded(File chapterDir, List<Page> pages) {
  67. return chapterDir.exists() && chapterDir.listFiles().length == pages.size();
  68. }
  69. private boolean isImageDownloaded(File imagePath) {
  70. return imagePath.exists() && !imagePath.isDirectory();
  71. }
  72. public Observable<Page> getDownloadedImage(final Page page, Source source, File chapterDir) {
  73. Observable<Page> obs = Observable.just(page);
  74. if (page.getImageUrl() == null)
  75. return obs;
  76. String imageFilename = getImageFilename(page);
  77. File imagePath = new File(chapterDir, imageFilename);
  78. if (!isImageDownloaded(imagePath)) {
  79. page.setStatus(Page.DOWNLOAD_IMAGE);
  80. obs = downloadImage(page, source, chapterDir, imageFilename);
  81. }
  82. return obs.flatMap(p -> {
  83. page.setImagePath(imagePath.getAbsolutePath());
  84. page.setStatus(Page.READY);
  85. return Observable.just(page);
  86. }).onErrorResumeNext(e -> {
  87. page.setStatus(Page.ERROR);
  88. return Observable.just(page);
  89. });
  90. }
  91. private Observable<Page> downloadImage(final Page page, Source source, File chapterDir, String imageFilename) {
  92. return source.getImageProgressResponse(page)
  93. .flatMap(resp -> {
  94. try {
  95. DiskUtils.saveBufferedSourceToDirectory(resp.body().source(), chapterDir, imageFilename);
  96. } catch (IOException e) {
  97. e.printStackTrace();
  98. throw new IllegalStateException("Unable to save image");
  99. }
  100. return Observable.just(page);
  101. });
  102. }
  103. public PublishSubject<DownloadChapterEvent> getDownloadsSubject() {
  104. return downloadsSubject;
  105. }
  106. }