<commit_before>import * as fs from 'fs';
import * as mio from '../default';

/**
 * Represents the download service.
 */
export let downloadService: mio.IDownloadService = {
  /**
   * Promises to download the chapter.
   * @param provider The provider.
   * @param series The series.
   * @param seriesPreviewImage The series preview image.
   * @param chapter The chapter.
   * @return The promise to download the chapter.
   */
  chapterAsync: async function(provider: mio.IProvider, series: mio.ISeries, seriesPreviewImage: mio.IBlob, chapter: mio.IChapter): Promise<void> {
    let chapterName = getChapterName(series, chapter);
    let chapterPath = getChapterPath(series, chapter);
    if (chapterName.hasValue && chapterPath.hasValue) {
      let chapterExists = await mio.promise<boolean>(callback => fs.exists(chapterPath.value, exists => callback(null, exists)));
      if (chapterExists.hasValue && chapterExists.hasValue && !chapterExists.value) {
        console.log(`Fetching ${chapterName.value}`);
        let beginTime = Date.now();
        let pages = await chapter.pagesAsync();
        await downloadService.pagesAsync(provider, series, seriesPreviewImage, chapter, pages);
        console.log(`Finished ${chapterName.value} ${prettyElapsedTime(beginTime)}`);
      }
    }
  },

  /**
   * Promises to download the pages.
   * @param provider The provider.
   * @param series The series.
   * @param seriesPreviewImage The series preview image.
   * @param chapter The chapter.
   * @param pages The pages.
   * @return The promise to download the pages.
   */
  pagesAsync: async function(provider: mio.IProvider, series: mio.ISeries, seriesPreviewImage: mio.IBlob, chapter: mio.IChapter, pages: mio.IPage[]): Promise<void> {
    let chapterPath = getChapterPath(series, chapter);
    if (chapterPath.hasValue) {
      let zip = mio.zipService.create(chapterPath.value);
      for (let page of pages) {
        let image = await page.imageAsync();
        let processedImage = await mio.imageService.processAsync(provider, image);
        if (processedImage.hasValue) {
          await zip.writeAsync(`${format(3, page.number)}.${mio.helperService.getImageExtension(processedImage.value)}`, processedImage.value);
        } else {
          throw new Error(`Invalid processed page #${page.number}`);
        }
      }
      await zip.writeAsync(`000.${mio.helperService.getImageExtension(seriesPreviewImage)}`, seriesPreviewImage);
      await zip.writeAsync('ComicInfo.xml', mio.metaService.createXml(series, chapter, pages));
      await zip.commitAsync();
    }
  },

  /**
   * Promises to download the series.
   * @param address The address.
   * @return The promise to download the series.
   */
  seriesAsync: async function(address: string): Promise<void> {
    let beginTime = Date.now();
    let provider = mio.openProvider(address);
    let series = await provider.seriesAsync(address);
    let seriesPreviewImage = await series.imageAsync();
    console.log(`Fetching ${series.title}`);
    for (let chapter of series.chapters) {
      await downloadService.chapterAsync(provider, series, seriesPreviewImage, chapter);
    }
    await cleanAsync(series);
    console.log(`Finished ${series.title} ${prettyElapsedTime(beginTime)}`);
  }
};

/**
 * Promises to clean obsolete chapters from the series.
 * @param series The series.
 * @return The promise to clean obsolete chapters from the series.
 */
async function cleanAsync(series: mio.ISeries): Promise<void> {
  let seriesName = getSeriesName(series);
  if (seriesName.hasValue) {
    let seriesExists = await mio.promise<boolean>(callback => fs.exists(seriesName.value, exists => callback(null, exists)));
    if (seriesExists.hasValue) {
      let fileNames = await mio.promise<string[]>(callback => fs.readdir(seriesName.value, callback));
      if (fileNames.hasValue) {
        let chapterPaths = series.chapters.map(chapter => getChapterPath(series, chapter).value);
        let filePaths = fileNames.value.map(fileName => `${seriesName.value}/${fileName}`);
        for (let filePath of filePaths) {
          if (chapterPaths.indexOf(filePath) === -1 && !/\.(mrdel|mrtmp)$/.test(filePath)) {
            await mio.promise<void>(callback => fs.rename(filePath, `${filePath}.mrdel`, callback));
          }
        }
      }
    }
  }
}

/**
 * Gets the chapter name.
 * @param series The series.
 * @param chapter The chapter.
 * @return The chapter name.
 */
function getChapterName(series: mio.ISeries, chapter: mio.IChapter): mio.IOption<string> {
  if (!chapter.number.hasValue) {<commit_msg>[Cli] Check if folder exists before cleaning (#41)<commit_after>import * as fs from 'fs';
import * as mio from '../default';

/**
 * Represents the download service.
 */
export let downloadService: mio.IDownloadService = {
  /**
   * Promises to download the chapter.
   * @param provider The provider.
   * @param series The series.
   * @param seriesPreviewImage The series preview image.
   * @param chapter The chapter.
   * @return The promise to download the chapter.
   */
  chapterAsync: async function(provider: mio.IProvider, series: mio.ISeries, seriesPreviewImage: mio.IBlob, chapter: mio.IChapter): Promise<void> {
    let chapterName = getChapterName(series, chapter);
    let chapterPath = getChapterPath(series, chapter);
    if (chapterName.hasValue && chapterPath.hasValue) {
      let chapterExists = await mio.promise<boolean>(callback => fs.exists(chapterPath.value, exists => callback(null, exists)));
      if (chapterExists.hasValue && chapterExists.hasValue && !chapterExists.value) {
        console.log(`Fetching ${chapterName.value}`);
        let beginTime = Date.now();
        let pages = await chapter.pagesAsync();
        await downloadService.pagesAsync(provider, series, seriesPreviewImage, chapter, pages);
        console.log(`Finished ${chapterName.value} ${prettyElapsedTime(beginTime)}`);
      }
    }
  },

  /**
   * Promises to download the pages.
   * @param provider The provider.
   * @param series The series.
   * @param seriesPreviewImage The series preview image.
   * @param chapter The chapter.
   * @param pages The pages.
   * @return The promise to download the pages.
   */
  pagesAsync: async function(provider: mio.IProvider, series: mio.ISeries, seriesPreviewImage: mio.IBlob, chapter: mio.IChapter, pages: mio.IPage[]): Promise<void> {
    let chapterPath = getChapterPath(series, chapter);
    if (chapterPath.hasValue) {
      let zip = mio.zipService.create(chapterPath.value);
      for (let page of pages) {
        let image = await page.imageAsync();
        let processedImage = await mio.imageService.processAsync(provider, image);
        if (processedImage.hasValue) {
          await zip.writeAsync(`${format(3, page.number)}.${mio.helperService.getImageExtension(processedImage.value)}`, processedImage.value);
        } else {
          throw new Error(`Invalid processed page #${page.number}`);
        }
      }
      await zip.writeAsync(`000.${mio.helperService.getImageExtension(seriesPreviewImage)}`, seriesPreviewImage);
      await zip.writeAsync('ComicInfo.xml', mio.metaService.createXml(series, chapter, pages));
      await zip.commitAsync();
    }
  },

  /**
   * Promises to download the series.
   * @param address The address.
   * @return The promise to download the series.
   */
  seriesAsync: async function(address: string): Promise<void> {
    let beginTime = Date.now();
    let provider = mio.openProvider(address);
    let series = await provider.seriesAsync(address);
    let seriesPreviewImage = await series.imageAsync();
    console.log(`Fetching ${series.title}`);
    for (let chapter of series.chapters) {
      await downloadService.chapterAsync(provider, series, seriesPreviewImage, chapter);
    }
    await cleanAsync(series);
    console.log(`Finished ${series.title} ${prettyElapsedTime(beginTime)}`);
  }
};

/**
 * Promises to clean obsolete chapters from the series.
 * @param series The series.
 * @return The promise to clean obsolete chapters from the series.
 */
async function cleanAsync(series: mio.ISeries): Promise<void> {
  let seriesName = getSeriesName(series);
  if (seriesName.hasValue) {
    let seriesExists = await mio.promise<boolean>(callback => fs.exists(seriesName.value, exists => callback(null, exists)));
    if (seriesExists.hasValue && seriesExists.value) {
      let fileNames = await mio.promise<string[]>(callback => fs.readdir(seriesName.value, callback));
      if (fileNames.hasValue) {
        let chapterPaths = series.chapters.map(chapter => getChapterPath(series, chapter).value);
        let filePaths = fileNames.value.map(fileName => `${seriesName.value}/${fileName}`);
        for (let filePath of filePaths) {
          if (chapterPaths.indexOf(filePath) === -1 && !/\.(mrdel|mrtmp)$/.test(filePath)) {
            await mio.promise<void>(callback => fs.rename(filePath, `${filePath}.mrdel`, callback));
          }
        }
      }
    }
  }
}

/**
 * Gets the chapter name.
 * @param series The series.
 * @param chapter The chapter.
 * @return The chapter name.
 */
function getChapterName(series: mio.ISeries, chapter: mio.IChapter): mio.IOption<string> {
  if (!chapter.number.hasValue) {
{'fraction_non_alphanumeric': 0.12806922660897782, 'fraction_numerical': 0.0012979989183342347, 'mean_word_length': 3.6206896551724137, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5896512', 'n_tokens_mistral': 2688, 'n_tokens_neox': 2672, 'n_words': 692}