Home Reference Source

src/class/ArticleImage.js

import axios from 'axios'

/**
 * A class for images in WeChat articles.
 *
 * You'll find this in the returned array of {@link WeChatArticle#getMedia}. You
 * can use `instanceof` to find out if that element is an image.
 *
 * This library itself doesn't handle images, since many problems are closely
 * related to your implementations. But we do provide a set of API for easier
 * image handling. You can get the URL, remove it out of the article and set a
 * new URL to where you caches all images. Additionally you can directly stream
 * the image, and clean up that `<img>` for cleaner markup.
 *
 * @since 0.4.0
 */
export default class ArticleImage {
  /**
   * Takes a cheerio object(`$(someSelector)`). You won't `new` one in most cases.
   * You can get all meta information of this image immediately.
   *
   * @param {$()} $img - a cheerio object representing an `<img>` in original WeChat
   * article.
   */
  constructor ($img) {
    this._$img = $img
    const size = $img.data('s')

    /** @type {number[]} */
    if (size) this.size = size.split(',').map(e => e * 1)

    /** @type {string} */
    this.type = $img.data('type') + ''

    /** @type {string} */
    this._origUrl = $img.data('src') + ''

    /** @type {string} */
    this.url = $img.data('src') + ''

    /** @type {number} */
    this.copyright = $img.data('copyright') * 1

    /** @type {number} */
    this.ratio = $img.data('ratio') * 1

    /** @type {number} */
    this.width = $img.data('w') * 1
    this._removed = false
  }

  /**
   * Cleans up the `<img>` tag. Actually removes all WeChat-defined `data-*`,
   * removes blank `style` and `class` attributes. And correct the URL to what
   * you set, in case some of your transformation breaks it.
   */
  cleanup () {
    if (!this._$img.attr('style')) this._$img.attr('style', null)
    if (!this._$img.attr('class')) this._$img.attr('class', null)
    this._$img.attr('data-type', null)
    this._$img.attr('data-copyright', null)
    this._$img.attr('data-src', null)
    this._$img.attr('data-w', null)
    this._$img.attr('data-ratio', null)
    this._$img.attr('data-s', null)
    this._$img.attr('src', this.url)
    // this._$img.attr('width', this.size ? this.size[0] : this.width * this.ratio)
  }

  /**
   * Removes current image from the HTML. This is useful when you decided to remove
   * some decorational gifs & images.
   */
  remove () {
    if (this._removed) throw new Error('This image has already been removed.')
    this._$img.remove()
    this._removed = true
  }

  /**
   * See if you've already removed it.
   */
  isRemoved () { return this._removed }

  /**
   * Get the underlying $(img). Good for custom tranformations. But don't set `src`
   * manually there. Try {@link ArticleImage#setUrl}.
   */
  get$Img () { return this._$img }

  /**
   * Set `src` for the picture. Please make sure this is correctly pointed to
   * where you store the image.
   */
  setUrl (newUrl) {
    this.url = newUrl + ''
    this._$img.attr('src', this.url)
  }

  /**
   * Returns a stream of that iamges. You can use this to persist images to disk.
   * @example
   * (await articleImage.stream()).pipe(fs.createWriteStream('xxx.jpg'))
   *
   * @return {Stream}
   */
  async stream () {
    const { data } = await axios.get(this._origUrl, { responseType: 'stream' })
    return data // Pipeable data stream.
  }
}