// Save image on disk
 private Observable<Page> downloadImage(
     Page page, Source source, File directory, String filename) {
   page.setStatus(Page.DOWNLOAD_IMAGE);
   return source
       .getImageProgressResponse(page)
       .flatMap(
           resp -> {
             try {
               DiskUtils.saveBufferedSourceToDirectory(resp.body().source(), directory, filename);
             } catch (Exception e) {
               Timber.e(e.getCause(), e.getMessage());
               return Observable.error(e);
             }
             return Observable.just(page);
           })
       .retry(2);
 }
  // Download the entire chapter
  private Observable<Download> downloadChapter(Download download) {
    try {
      DiskUtils.createDirectory(download.directory);
    } catch (IOException e) {
      return Observable.error(e);
    }

    Observable<List<Page>> pageListObservable =
        download.pages == null
            ?
            // Pull page list from network and add them to download object
            download
                .source
                .pullPageListFromNetwork(download.chapter.url)
                .doOnNext(pages -> download.pages = pages)
                .doOnNext(pages -> savePageList(download))
            :
            // Or if the page list already exists, start from the file
            Observable.just(download.pages);

    return pageListObservable
        .subscribeOn(Schedulers.io())
        .doOnNext(
            pages -> {
              download.downloadedImages = 0;
              download.setStatus(Download.DOWNLOADING);
            })
        // Get all the URLs to the source images, fetch pages if necessary
        .flatMap(download.source::getAllImageUrlsFromPageList)
        // Start downloading images, consider we can have downloaded images already
        .concatMap(page -> getOrDownloadImage(page, download))
        // Do after download completes
        .doOnCompleted(() -> onDownloadCompleted(download))
        .toList()
        .map(pages -> download)
        // If the page list threw, it will resume here
        .onErrorResumeNext(
            error -> {
              download.setStatus(Download.ERROR);
              return Observable.just(download);
            });
  }
 public void deleteChapter(Source source, Manga manga, Chapter chapter) {
   File path = getAbsoluteChapterDirectory(source, manga, chapter);
   DiskUtils.deleteFiles(path);
 }