Home Reference Source

src/api/SearchFetcher.js

import { SEARCH as ENDPOINT } from '../Endpoint';
import Fetcher from './Fetcher';

/**
 * Search API.
 * @see https://docs-en.kkbox.codes/v1.1/reference#search
 */
export default class SearchFetcher extends Fetcher {
  /**
   * @ignore
   */
  constructor(http, territory = 'TW') {
    super(http, territory);

    /**
     * @ignore
     */
    this.filterConditions = undefined;

    /**
     * @ignore
     */
    this.q = undefined;

    /**
     * @ignore
     */
    this.type = undefined;
  }

  /**
   * Filter what you don't want when search.
   *
   * @param {Object} [conditions] - search conditions.
   * @param {string} conditions.track - track's name.
   * @param {string} conditions.album - album's name.
   * @param {string} conditions.artist - artist's name.
   * @param {string} conditions.playlist - playlist's title.
   * @param {string} conditions.availableTerritory - tracks and albums available territory.
   * @return {Search}
   * @example
   * api.searchFetcher
   *  .setSearchCriteria('五月天 好好')
   *  .filter({artist: '五月天'})
   *  .fetchSearchResult();
   */
  filter(conditions = {}) {
    this.filterConditions = conditions;
    return this;
  }

  /**
   * Init the search fetcher for the artist, album, track or playlist.
   *
   * @param {string} q - The keyword to be searched.
   * @param {string} [type] - ['artist', 'album', 'track', 'playlist'] The type of search. Default to search all types. If you want to use multiple type at the same time, you may use ',' to separate them.
   * @return {Search}
   * @see https://docs-en.kkbox.codes/v1.1/reference#search_1
   */
  setSearchCriteria(q, type = undefined) {
    this.q = q;
    this.type = type;
    return this;
  }

  /**
   * Fetch the search result.
   *
   * @param {number} [limit] - The size of one page.
   * @param {number} [offset] - The offset index for first element.
   * @return {Promise}
   * @example
   * api.searchFetcher
   *  .setSearchCriteria('五月天 好好')
   *  .fetchSearchResult();
   * @see https://docs-en.kkbox.codes/v1.1/reference#search_1
   */
  fetchSearchResult(limit = undefined, offset = undefined) {
    return this.http
      .get(ENDPOINT, {
        q: this.q,
        type: this.type,
        territory: this.territory,
        limit: limit,
        offset: offset
      })
      .then(doFilter.bind(this));
  }
}

function doFilter(response) {
  if (this.filterConditions !== undefined) {
    const data = Object.keys(response.data).map(key => {
      switch (key) {
        case 'tracks':
          return {
            [key]: Object.assign(response.data[key], {
              data: response.data[key].data.filter(track => {
                if (
                  this.filterConditions.availableTerritory !== undefined &&
                  !track.available_territories.includes(
                    this.filterConditions.availableTerritory
                  )
                ) {
                  return false;
                }
                if (
                  this.filterConditions.track !== undefined &&
                  !new RegExp('.*' + this.filterConditions.track + '.*').test(
                    track.name
                  )
                ) {
                  return false;
                }
                if (
                  this.filterConditions.album !== undefined &&
                  !new RegExp('.*' + this.filterConditions.album + '.*').test(
                    track.album.name
                  )
                ) {
                  return false;
                }
                return !(
                  this.filterConditions.artist !== undefined &&
                  !new RegExp('.*' + this.filterConditions.artist + '.*').test(
                    track.album.artist.name
                  )
                );
              })
            })
          };
        case 'albums':
          return {
            [key]: Object.assign(response.data[key], {
              data: response.data[key].data.filter(album => {
                if (
                  this.filterConditions.availableTerritory !== undefined &&
                  !album.available_territories.includes(
                    this.filterConditions.availableTerritory
                  )
                ) {
                  return false;
                }
                if (
                  this.filterConditions.album !== undefined &&
                  !new RegExp('.*' + this.filterConditions.album + '.*').test(
                    album.name
                  )
                ) {
                  return false;
                }
                return !(
                  this.filterConditions.artist !== undefined &&
                  !new RegExp('.*' + this.filterConditions.artist + '.*').test(
                    album.artist.name
                  )
                );
              })
            })
          };
        case 'artists':
          return {
            [key]: Object.assign(response.data[key], {
              data: response.data[key].data.filter(artist => {
                if (this.filterConditions.artist === undefined) {
                  return true;
                } else {
                  return new RegExp(
                    '.*' + this.filterConditions.artist + '.*'
                  ).test(artist.name);
                }
              })
            })
          };
        case 'playlists':
          return {
            [key]: Object.assign(response.data[key], {
              data: response.data[key].data.filter(playlist => {
                if (this.filterConditions.playlist === undefined) {
                  return true;
                } else {
                  return new RegExp(
                    '.*' + this.filterConditions.playlist + '.*'
                  ).test(playlist.title);
                }
              })
            })
          };
        default:
          return {
            [key]: response.data[key]
          };
      }
    });
    return Object.assign(response, {
      data: Object.assign(...data)
    });
  } else {
    return response;
  }
}