services_HttpClient.ts

import ky, { Options } from 'ky';
import { Utils } from '../HLSUtils.js';
import InvalidPlayList from './../exceptions/InvalidPlaylist.js';

/**
 * @category Services
 * @author Nur Rony<pro.nmrony@gmail.com>
 * Internal service wrapper for handling specialized HLS network requests via `ky`.
 */
class HttpClient {
  /** Stores the sanitized ky options. */
  private options: Options;

  static defaultKyOptions: Options = { retry: { limit: 0 } };

  /**
   */
  static unSupportedOptions: string[] = [
    'uri',
    'url',
    'json',
    'form',
    'body',
    'method',
    'setHost',
    'isStream',
    'parseJson',
    'prefixUrl',
    'cookieJar',
    'playlistURL',
    'concurrency',
    'allowGetBody',
    'stringifyJson',
    'methodRewriting',
  ];

  /**
   * Constructor of HttpClient
   * @param customOptions - User-provided ky configuration options.
   */
  constructor(customOptions: Record<string, any> = {}) {
    this.options = Object.assign(
      {},
      HttpClient.defaultKyOptions,
      Utils.omit(customOptions, ...(HttpClient.unSupportedOptions as any))
    );
  }

  /**
   * Fetches content as plain text and validates it as a valid HLS playlist.
   * @param url - The URL to fetch.
   * @throws {InvalidPlayList} Throws if content fails HLS validation.
   * @returns The validated playlist body.
   */
  async fetchText(url: string): Promise<string> {
    const body = await ky.get(url, { ...this.options }).text();
    if (!Utils.isValidPlaylist(body)) {
      throw new InvalidPlayList('Invalid playlist');
    }
    return body;
  }

  /**
   * Fetches a resource and returns its body as a stream.
   * @param url - The URL of the segment or file.
   * @returns The response body stream.
   */
  async getStream(url: string): Promise<ReadableStream<Uint8Array>> {
    const response = await ky.get(url, { ...this.options });
    if (!response.body) {
      throw new Error('Response body is null');
    }
    return response.body;
  }
}

export default HttpClient;