Source.java 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package eu.kanade.mangafeed.sources;
  2. import com.squareup.okhttp.Headers;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import eu.kanade.mangafeed.data.caches.CacheManager;
  6. import eu.kanade.mangafeed.data.helpers.NetworkHelper;
  7. import eu.kanade.mangafeed.data.models.Chapter;
  8. import eu.kanade.mangafeed.data.models.Manga;
  9. import eu.kanade.mangafeed.data.models.Page;
  10. import rx.Observable;
  11. import rx.schedulers.Schedulers;
  12. public abstract class Source {
  13. // Methods to implement or optionally override
  14. // Name of the source to display
  15. public abstract String getName();
  16. // Id of the source (must be declared and obtained from SourceManager to avoid conflicts)
  17. public abstract int getSourceId();
  18. protected abstract String getUrlFromPageNumber(int page);
  19. protected abstract String getSearchUrl(String query, int page);
  20. protected abstract List<Manga> parsePopularMangasFromHtml(String unparsedHtml);
  21. protected abstract List<Manga> parseSearchFromHtml(String unparsedHtml);
  22. protected abstract Manga parseHtmlToManga(String mangaUrl, String unparsedHtml);
  23. protected abstract List<Chapter> parseHtmlToChapters(String unparsedHtml);
  24. protected abstract List<String> parseHtmlToPageUrls(String unparsedHtml);
  25. protected abstract String parseHtmlToImageUrl(String unparsedHtml);
  26. // True if the source requires a login
  27. public boolean isLoginRequired() {
  28. return false;
  29. }
  30. // Get the URL to the details of a manga, useful if the source provides some kind of API or fast calls
  31. protected String getMangaUrl(String defaultMangaUrl) {
  32. return defaultMangaUrl;
  33. }
  34. // Get the URL of the first page that contains a source image and the page list
  35. protected String getChapterPageUrl(String defaultPageUrl) {
  36. return defaultPageUrl;
  37. }
  38. // Get the URL of the remaining pages that contains source images
  39. protected String getRemainingPagesUrl(String defaultPageUrl) {
  40. return defaultPageUrl;
  41. }
  42. // Default headers, it can be overriden by children or just add new keys
  43. protected Headers.Builder headersBuilder() {
  44. Headers.Builder builder = new Headers.Builder();
  45. builder.add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)");
  46. return builder;
  47. }
  48. // Number of images to download at the same time
  49. protected int getNumberOfConcurrentPageDownloads() {
  50. return 3;
  51. }
  52. // ***** Source class implementation *****
  53. protected NetworkHelper mNetworkService;
  54. protected CacheManager mCacheManager;
  55. protected Headers mRequestHeaders;
  56. public Source(NetworkHelper networkService, CacheManager cacheManager) {
  57. mNetworkService = networkService;
  58. mCacheManager = cacheManager;
  59. mRequestHeaders = headersBuilder().build();
  60. }
  61. // Get the most popular mangas from the source
  62. public Observable<List<Manga>> pullPopularMangasFromNetwork(int page) {
  63. String url = getUrlFromPageNumber(page);
  64. return mNetworkService
  65. .getStringResponse(url, mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders)
  66. .flatMap(response -> Observable.just(parsePopularMangasFromHtml(response)));
  67. }
  68. // Get mangas from the source with a query
  69. public Observable<List<Manga>> searchMangasFromNetwork(String query, int page) {
  70. return mNetworkService
  71. .getStringResponse(getSearchUrl(query, page), mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders)
  72. .flatMap(response -> Observable.just(parseSearchFromHtml(response)));
  73. }
  74. // Get manga details from the source
  75. public Observable<Manga> pullMangaFromNetwork(final String mangaUrl) {
  76. return mNetworkService
  77. .getStringResponse(getMangaUrl(mangaUrl), mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders)
  78. .flatMap(unparsedHtml -> Observable.just(parseHtmlToManga(mangaUrl, unparsedHtml)));
  79. }
  80. // Get chapter list of a manga from the source
  81. public Observable<List<Chapter>> pullChaptersFromNetwork(String mangaUrl) {
  82. return mNetworkService
  83. .getStringResponse(mangaUrl, mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders)
  84. .flatMap(unparsedHtml ->
  85. Observable.just(parseHtmlToChapters(unparsedHtml)));
  86. }
  87. public Observable<List<Page>> pullPageListFromNetwork(final String chapterUrl) {
  88. return mCacheManager.getPageUrlsFromDiskCache(chapterUrl)
  89. .onErrorResumeNext(throwable -> {
  90. return mNetworkService
  91. .getStringResponse(getChapterPageUrl(chapterUrl), mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders)
  92. .flatMap(unparsedHtml -> {
  93. List<String> pageUrls = parseHtmlToPageUrls(unparsedHtml);
  94. return Observable.just(getFirstImageFromPageUrls(pageUrls, unparsedHtml));
  95. })
  96. .doOnNext(pages -> savePageList(chapterUrl, pages));
  97. })
  98. .onBackpressureBuffer();
  99. }
  100. // Get the URLs of the images of a chapter
  101. public Observable<Page> getRemainingImageUrlsFromPageList(final List<Page> pages) {
  102. return Observable.from(pages)
  103. .filter(page -> page.getImageUrl() == null)
  104. .window(getNumberOfConcurrentPageDownloads())
  105. .concatMap(batchedPages ->
  106. batchedPages.concatMap(this::getImageUrlFromPage)
  107. );
  108. }
  109. private Observable<Page> getImageUrlFromPage(final Page page) {
  110. return mNetworkService
  111. .getStringResponse(getRemainingPagesUrl(page.getUrl()), mNetworkService.NULL_CACHE_CONTROL, mRequestHeaders)
  112. .flatMap(unparsedHtml -> Observable.just(parseHtmlToImageUrl(unparsedHtml)))
  113. .flatMap(imageUrl -> {
  114. page.setImageUrl(imageUrl);
  115. return Observable.just(page);
  116. })
  117. .subscribeOn(Schedulers.io());
  118. }
  119. public void savePageList(String chapterUrl, List<Page> pages) {
  120. mCacheManager.putPageUrlsToDiskCache(chapterUrl, pages);
  121. }
  122. private List<Page> convertToPages(List<String> pageUrls) {
  123. List<Page> pages = new ArrayList<>();
  124. for (int i = 0; i < pageUrls.size(); i++) {
  125. pages.add(new Page(i, pageUrls.get(i)));
  126. }
  127. return pages;
  128. }
  129. private List<Page> getFirstImageFromPageUrls(List<String> pageUrls, String unparsedHtml) {
  130. List<Page> pages = convertToPages(pageUrls);
  131. String firstImage = parseHtmlToImageUrl(unparsedHtml);
  132. pages.get(0).setImageUrl(firstImage);
  133. return pages;
  134. }
  135. }