{"version":3,"file":"index.cjs","names":["r","n","o","i","l","i","e","l","t","o","t","s","r"],"sources":["../src/errors/index.ts","../src/models/fetched-transcript.ts","../src/parsers/transcript-parser.ts","../src/utils/http-error-handler.ts","../src/models/transcript.ts","../src/models/transcript-list.ts","../src/types/index.ts","../src/fetchers/youtube-data-extractor.ts","../src/fetchers/transcript-list-fetcher.ts","../../../node_modules/.bun/grab-url@1.6.22/node_modules/grab-url/dist/log.es.js","../../../node_modules/.bun/grab-url@1.6.22/node_modules/grab-url/dist/core-BX1uvK6L.js","../../../node_modules/.bun/grab-url@1.6.22/node_modules/grab-url/dist/grab-api.es.js","../src/http/fetch-http-client.ts","../src/youtube-transcript-api.ts","../src/utils/transcript-utils.ts","../src/proxies/index.ts","../src/formatters/index.ts"],"sourcesContent":["/**\r\n * @fileoverview All error classes for the YouTube Transcript API\r\n */\r\n\r\nimport type { ProxyConfig } from '../proxies';\r\n\r\nconst YOUTUBE_WATCH_URL = 'https://www.youtube.com/watch?v={video_id}';\r\n\r\n/**\r\n * Base exception class for all YouTube Transcript API errors.\r\n */\r\nexport class YouTubeTranscriptApiException extends Error {\r\n  constructor(message: string) {\r\n    super(message);\r\n    this.name = 'YouTubeTranscriptApiException';\r\n    Object.setPrototypeOf(this, YouTubeTranscriptApiException.prototype);\r\n  }\r\n}\r\n\r\n/**\r\n * Base exception class for transcript retrieval errors.\r\n */\r\nexport class CouldNotRetrieveTranscript extends YouTubeTranscriptApiException {\r\n  private static readonly ERROR_MESSAGE = '\\nCould not retrieve a transcript for the video {video_url}!';\r\n  private static readonly CAUSE_MESSAGE_INTRO = ' This is most likely caused by:\\n\\n{cause}';\r\n  private static readonly GITHUB_REFERRAL =\r\n    '\\n\\nIf you are sure that the described cause is not responsible for this error ' +\r\n    'and that a transcript should be retrievable, please create an issue at ' +\r\n    'https://github.com/jdepoix/youtube-transcript-api/issues. ' +\r\n    'Please add which version of youtube_transcript_api you are using ' +\r\n    'and provide the information needed to replicate the error. ' +\r\n    'Also make sure that there are no open issues which already describe your problem!';\r\n\r\n  public videoId: string;\r\n  protected causeMessage: string = '';\r\n\r\n  constructor(videoId: string) {\r\n    super('');\r\n    this.videoId = videoId;\r\n    Object.setPrototypeOf(this, CouldNotRetrieveTranscript.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n\r\n  protected buildErrorMessage(): string {\r\n    const videoUrl = YOUTUBE_WATCH_URL.replace('{video_id}', this.videoId);\r\n    let errorMessage = CouldNotRetrieveTranscript.ERROR_MESSAGE.replace('{video_url}', videoUrl);\r\n\r\n    const cause = this.getCause();\r\n    if (cause) {\r\n      errorMessage += CouldNotRetrieveTranscript.CAUSE_MESSAGE_INTRO.replace('{cause}', cause);\r\n      errorMessage += CouldNotRetrieveTranscript.GITHUB_REFERRAL;\r\n    }\r\n\r\n    return errorMessage;\r\n  }\r\n\r\n  protected getCause(): string {\r\n    return this.causeMessage;\r\n  }\r\n}\r\n\r\n// Cookie errors\r\nexport class CookieError extends YouTubeTranscriptApiException {\r\n  constructor(message: string) {\r\n    super(message);\r\n    this.name = 'CookieError';\r\n    Object.setPrototypeOf(this, CookieError.prototype);\r\n  }\r\n}\r\n\r\nexport class CookiePathInvalid extends CookieError {\r\n  constructor(cookiePath: string) {\r\n    super(`Can't load the provided cookie file: ${cookiePath}`);\r\n    this.name = 'CookiePathInvalid';\r\n    Object.setPrototypeOf(this, CookiePathInvalid.prototype);\r\n  }\r\n}\r\n\r\nexport class CookieInvalid extends CookieError {\r\n  constructor(cookiePath: string) {\r\n    super(`The cookies provided are not valid (may have expired): ${cookiePath}`);\r\n    this.name = 'CookieInvalid';\r\n    Object.setPrototypeOf(this, CookieInvalid.prototype);\r\n  }\r\n}\r\n\r\n// Video errors\r\nexport class YouTubeDataUnparsable extends CouldNotRetrieveTranscript {\r\n  protected causeMessage =\r\n    'The data required to fetch the transcript is not parsable. This should ' +\r\n    'not happen, please open an issue (make sure to include the video ID)!';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'YouTubeDataUnparsable';\r\n    Object.setPrototypeOf(this, YouTubeDataUnparsable.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n}\r\n\r\nexport class YouTubeRequestFailed extends CouldNotRetrieveTranscript {\r\n  private httpErrorReason: string;\r\n\r\n  constructor(videoId: string, httpError: Error) {\r\n    super(videoId);\r\n    this.httpErrorReason = httpError.message;\r\n    this.name = 'YouTubeRequestFailed';\r\n    this.message = this.buildErrorMessage();\r\n    Object.setPrototypeOf(this, YouTubeRequestFailed.prototype);\r\n  }\r\n\r\n  protected getCause(): string {\r\n    return `Request to YouTube failed: ${this.httpErrorReason}`;\r\n  }\r\n}\r\n\r\nexport class VideoUnplayable extends CouldNotRetrieveTranscript {\r\n  private unplayableReason: string | null;\r\n  private additionalDetails: string[];\r\n\r\n  constructor(videoId: string, unplayableReason: string | null, additionalDetails: string[]) {\r\n    super(videoId);\r\n    this.unplayableReason = unplayableReason;\r\n    this.additionalDetails = additionalDetails;\r\n    this.name = 'VideoUnplayable';\r\n    this.message = this.buildErrorMessage();\r\n    Object.setPrototypeOf(this, VideoUnplayable.prototype);\r\n  }\r\n\r\n  protected getCause(): string {\r\n    let reason = this.unplayableReason ?? 'No reason specified!';\r\n\r\n    if (this.additionalDetails.length > 0) {\r\n      const formattedDetails = this.additionalDetails.map(detail => ` - ${detail}`).join('\\n');\r\n      reason = `${reason}\\n\\nAdditional Details:\\n${formattedDetails}`;\r\n    }\r\n\r\n    return `The video is unplayable for the following reason: ${reason}`;\r\n  }\r\n}\r\n\r\nexport class VideoUnavailable extends CouldNotRetrieveTranscript {\r\n  protected causeMessage = 'The video is no longer available';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'VideoUnavailable';\r\n    Object.setPrototypeOf(this, VideoUnavailable.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n}\r\n\r\nexport class InvalidVideoId extends CouldNotRetrieveTranscript {\r\n  protected causeMessage =\r\n    'You provided an invalid video id. Make sure you are using the video id and NOT the url!\\n\\n' +\r\n    'Do NOT run: `new YouTubeTranscriptApi().fetch(\"https://www.youtube.com/watch?v=1234\")`\\n' +\r\n    'Instead run: `new YouTubeTranscriptApi().fetch(\"1234\")`';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'InvalidVideoId';\r\n    Object.setPrototypeOf(this, InvalidVideoId.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n}\r\n\r\nexport class AgeRestricted extends CouldNotRetrieveTranscript {\r\n  protected causeMessage =\r\n    'This video is age-restricted. Therefore, you are unable to retrieve ' +\r\n    'transcripts for it without authenticating yourself.\\n\\n' +\r\n    'Unfortunately, Cookie Authentication is temporarily unsupported in ' +\r\n    'youtube-transcript-api, as recent changes in YouTube\\'s API broke the previous ' +\r\n    'implementation. I will do my best to re-implement it as soon as possible.';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'AgeRestricted';\r\n    Object.setPrototypeOf(this, AgeRestricted.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n}\r\n\r\nexport class FailedToCreateConsentCookie extends CouldNotRetrieveTranscript {\r\n  protected causeMessage = 'Failed to automatically give consent to saving cookies';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'FailedToCreateConsentCookie';\r\n    Object.setPrototypeOf(this, FailedToCreateConsentCookie.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n}\r\n\r\n// Transcript errors\r\nexport class TranscriptsDisabled extends CouldNotRetrieveTranscript {\r\n  protected causeMessage = 'Subtitles are disabled for this video';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'TranscriptsDisabled';\r\n    Object.setPrototypeOf(this, TranscriptsDisabled.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n}\r\n\r\nexport class NotTranslatable extends CouldNotRetrieveTranscript {\r\n  protected causeMessage = 'The requested language is not translatable';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'NotTranslatable';\r\n    Object.setPrototypeOf(this, NotTranslatable.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n}\r\n\r\nexport class TranslationLanguageNotAvailable extends CouldNotRetrieveTranscript {\r\n  protected causeMessage = 'The requested translation language is not available';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'TranslationLanguageNotAvailable';\r\n    Object.setPrototypeOf(this, TranslationLanguageNotAvailable.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n}\r\n\r\nexport class NoTranscriptFound extends CouldNotRetrieveTranscript {\r\n  private requestedLanguageCodes: string[];\r\n  private availableTranscriptData: any;\r\n\r\n  constructor(videoId: string, requestedLanguageCodes: string[], availableTranscriptData: any) {\r\n    super(videoId);\r\n    this.requestedLanguageCodes = requestedLanguageCodes;\r\n    this.availableTranscriptData = availableTranscriptData;\r\n    this.name = 'NoTranscriptFound';\r\n    this.message = this.buildErrorMessage();\r\n    Object.setPrototypeOf(this, NoTranscriptFound.prototype);\r\n  }\r\n\r\n  protected getCause(): string {\r\n    const requestedLanguages = this.requestedLanguageCodes.join(', ');\r\n    return (\r\n      `No transcripts were found for any of the requested language codes: ${requestedLanguages}\\n\\n` +\r\n      this.availableTranscriptData.toString()\r\n    );\r\n  }\r\n}\r\n\r\nexport class PoTokenRequired extends CouldNotRetrieveTranscript {\r\n  protected causeMessage =\r\n    'The requested video cannot be retrieved without a PO Token. If this happens, ' +\r\n    'please open a GitHub issue!';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'PoTokenRequired';\r\n    Object.setPrototypeOf(this, PoTokenRequired.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n}\r\n\r\n// Network errors\r\nexport class RequestBlocked extends CouldNotRetrieveTranscript {\r\n  private static readonly BASE_CAUSE_MESSAGE =\r\n    'YouTube is blocking requests from your IP. This usually is due to one of the ' +\r\n    'following reasons:\\n' +\r\n    '- You have done too many requests and your IP has been blocked by YouTube\\n' +\r\n    '- You are doing requests from an IP belonging to a cloud provider (like AWS, ' +\r\n    'Google Cloud Platform, Azure, etc.). Unfortunately, most IPs from cloud ' +\r\n    'providers are blocked by YouTube.\\n\\n';\r\n\r\n  private static readonly DEFAULT_CAUSE_MESSAGE =\r\n    RequestBlocked.BASE_CAUSE_MESSAGE +\r\n    'There are two things you can do to work around this:\\n' +\r\n    '1. Use proxies to hide your IP address, as explained in the \"Working around ' +\r\n    'IP bans\" section of the README ' +\r\n    '(https://github.com/jdepoix/youtube-transcript-api' +\r\n    '?tab=readme-ov-file' +\r\n    '#working-around-ip-bans-requestblocked-or-ipblocked-exception).\\n' +\r\n    '2. (NOT RECOMMENDED) If you authenticate your requests using cookies, you ' +\r\n    'will be able to continue doing requests for a while. However, YouTube will ' +\r\n    'eventually permanently ban the account that you have used to authenticate ' +\r\n    'with! So only do this if you don\\'t mind your account being banned!';\r\n\r\n  private static readonly WITH_GENERIC_PROXY_CAUSE_MESSAGE =\r\n    'YouTube is blocking your requests, despite you using proxies. Keep in mind ' +\r\n    'that a proxy is just a way to hide your real IP behind the IP of that proxy, ' +\r\n    'but there is no guarantee that the IP of that proxy won\\'t be blocked as ' +\r\n    'well.\\n\\n' +\r\n    'The only truly reliable way to prevent IP blocks is rotating through a large ' +\r\n    'pool of residential IPs, by using a provider like Webshare ' +\r\n    '(https://www.webshare.io/?referral_code=w0xno53eb50g), which provides you ' +\r\n    'with a pool of >30M residential IPs (make sure to purchase ' +\r\n    '\"Residential\" proxies, NOT \"Proxy Server\" or \"Static Residential\"!).\\n\\n' +\r\n    'You will find more information on how to easily integrate Webshare here: ' +\r\n    'https://github.com/jdepoix/youtube-transcript-api' +\r\n    '?tab=readme-ov-file#using-webshare';\r\n\r\n  private static readonly WITH_WEBSHARE_PROXY_CAUSE_MESSAGE =\r\n    'YouTube is blocking your requests, despite you using Webshare proxies. ' +\r\n    'Please make sure that you have purchased \"Residential\" proxies and ' +\r\n    'NOT \"Proxy Server\" or \"Static Residential\", as those won\\'t work as ' +\r\n    'reliably! The free tier also uses \"Proxy Server\" and will NOT work!\\n\\n' +\r\n    'The only reliable option is using \"Residential\" proxies (not \"Static ' +\r\n    'Residential\"), as this allows you to rotate through a pool of over 30M IPs, ' +\r\n    'which means you will always find an IP that hasn\\'t been blocked by YouTube ' +\r\n    'yet!\\n\\n' +\r\n    'You can support the development of this open source project by making your ' +\r\n    'Webshare purchases through this affiliate link: ' +\r\n    'https://www.webshare.io/?referral_code=w0xno53eb50g \\n\\n' +\r\n    'Thank you for your support! <3';\r\n\r\n  private proxyConfig: ProxyConfig | null = null;\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'RequestBlocked';\r\n    Object.setPrototypeOf(this, RequestBlocked.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n\r\n  withProxyConfig(proxyConfig: ProxyConfig | null): RequestBlocked {\r\n    this.proxyConfig = proxyConfig;\r\n    this.message = this.buildErrorMessage();\r\n    return this;\r\n  }\r\n\r\n  protected getCause(): string {\r\n    // Check class name to avoid circular dependency with instanceof\r\n    if (this.proxyConfig && this.proxyConfig.constructor.name === 'WebshareProxyConfig') {\r\n      return RequestBlocked.WITH_WEBSHARE_PROXY_CAUSE_MESSAGE;\r\n    }\r\n    if (this.proxyConfig && this.proxyConfig.constructor.name === 'GenericProxyConfig') {\r\n      return RequestBlocked.WITH_GENERIC_PROXY_CAUSE_MESSAGE;\r\n    }\r\n    return RequestBlocked.DEFAULT_CAUSE_MESSAGE;\r\n  }\r\n}\r\n\r\nexport class IpBlocked extends RequestBlocked {\r\n  protected causeMessage =\r\n    'YouTube is blocking requests from your IP. This usually is due to one of the ' +\r\n    'following reasons:\\n' +\r\n    '- You have done too many requests and your IP has been blocked by YouTube\\n' +\r\n    '- You are doing requests from an IP belonging to a cloud provider (like AWS, ' +\r\n    'Google Cloud Platform, Azure, etc.). Unfortunately, most IPs from cloud ' +\r\n    'providers are blocked by YouTube.\\n\\n' +\r\n    'Ways to work around this are explained in the \"Working around IP ' +\r\n    'bans\" section of the README (https://github.com/jdepoix/youtube-transcript-api' +\r\n    '?tab=readme-ov-file' +\r\n    '#working-around-ip-bans-requestblocked-or-ipblocked-exception).\\n';\r\n\r\n  constructor(videoId: string) {\r\n    super(videoId);\r\n    this.name = 'IpBlocked';\r\n    Object.setPrototypeOf(this, IpBlocked.prototype);\r\n    this.message = this.buildErrorMessage();\r\n  }\r\n\r\n  protected getCause(): string {\r\n    return this.causeMessage;\r\n  }\r\n}\r\n","/**\r\n * @fileoverview FetchedTranscript model representing a complete transcript with all snippets\r\n */\r\n\r\nimport { FetchedTranscriptSnippet } from '../types';\r\n\r\n/**\r\n * Represents a fetched transcript with all its snippets and metadata.\r\n * This class is iterable, allowing you to iterate over transcript snippets.\r\n *\r\n * @example\r\n * ```typescript\r\n * const transcript = await api.fetch('video_id');\r\n * for (const snippet of transcript) {\r\n *   console.log(snippet.text);\r\n * }\r\n * ```\r\n */\r\nexport class FetchedTranscript implements Iterable<FetchedTranscriptSnippet> {\r\n  /** Array of transcript snippets */\r\n  public readonly snippets: FetchedTranscriptSnippet[];\r\n\r\n  /** The video ID this transcript belongs to */\r\n  public readonly videoId: string;\r\n\r\n  /** The language name (e.g., \"English\") */\r\n  public readonly language: string;\r\n\r\n  /** The language code (e.g., \"en\") */\r\n  public readonly languageCode: string;\r\n\r\n  /** Whether this transcript was automatically generated */\r\n  public readonly isGenerated: boolean;\r\n\r\n  /**\r\n   * Creates a new FetchedTranscript instance.\r\n   *\r\n   * @param {FetchedTranscriptSnippet[]} snippets - Array of transcript snippets\r\n   * @param {string} videoId - The video ID\r\n   * @param {string} language - The language name\r\n   * @param {string} languageCode - The language code\r\n   * @param {boolean} isGenerated - Whether the transcript is auto-generated\r\n   */\r\n  constructor(\r\n    snippets: FetchedTranscriptSnippet[],\r\n    videoId: string,\r\n    language: string,\r\n    languageCode: string,\r\n    isGenerated: boolean\r\n  ) {\r\n    this.snippets = snippets;\r\n    this.videoId = videoId;\r\n    this.language = language;\r\n    this.languageCode = languageCode;\r\n    this.isGenerated = isGenerated;\r\n  }\r\n\r\n  /**\r\n   * Makes the transcript iterable, allowing for...of loops.\r\n   *\r\n   * @returns {Iterator<FetchedTranscriptSnippet>} Iterator over the snippets\r\n   */\r\n  [Symbol.iterator](): Iterator<FetchedTranscriptSnippet> {\r\n    return this.snippets[Symbol.iterator]();\r\n  }\r\n\r\n  /**\r\n   * Gets a snippet by index.\r\n   *\r\n   * @param {number} index - The index of the snippet\r\n   * @returns {FetchedTranscriptSnippet} The snippet at the given index\r\n   */\r\n  getSnippetAtIndex(index: number): FetchedTranscriptSnippet {\r\n    return this.snippets[index];\r\n  }\r\n\r\n  /**\r\n   * Alias for getSnippetAtIndex() for backward compatibility.\r\n   *\r\n   * @deprecated Use getSnippetAtIndex() instead\r\n   * @param {number} index - The index of the snippet\r\n   * @returns {FetchedTranscriptSnippet} The snippet at the given index\r\n   */\r\n  get(index: number): FetchedTranscriptSnippet {\r\n    return this.getSnippetAtIndex(index);\r\n  }\r\n\r\n  /**\r\n   * Gets the total number of snippets in the transcript.\r\n   *\r\n   * @returns {number} The number of snippets\r\n   */\r\n  get length(): number {\r\n    return this.snippets.length;\r\n  }\r\n\r\n  /**\r\n   * Converts the transcript to raw data format (array of objects).\r\n   * This is useful for serialization or when you need plain data objects.\r\n   *\r\n   * @returns {Array<{text: string, start: number, duration: number}>} Raw transcript data\r\n   */\r\n  toRawData(): Array<{ text: string; start: number; duration: number }> {\r\n    return this.snippets.map(snippet => ({\r\n      text: snippet.text,\r\n      start: snippet.start,\r\n      duration: snippet.duration\r\n    }));\r\n  }\r\n}\r\n","/**\r\n * @fileoverview Parser for converting YouTube transcript XML data into structured snippets\r\n */\r\n\r\nimport { XMLParser } from 'fast-xml-parser';\r\nimport { decode } from 'html-entities';\r\nimport { FetchedTranscriptSnippet } from '../types';\r\n\r\n/**\r\n * Parses transcript XML data from YouTube into FetchedTranscriptSnippet objects.\r\n *\r\n * @example\r\n * ```typescript\r\n * const parser = new TranscriptParser(false);\r\n * const snippets = parser.parseTranscriptXml(xmlData);\r\n * ```\r\n */\r\nexport class TranscriptParser {\r\n  /**\r\n   * HTML formatting tags that can be preserved in transcript text.\r\n   * These are common formatting tags that provide semantic meaning.\r\n   */\r\n  private static readonly FORMATTING_TAGS = [\r\n    'strong',  // Bold text\r\n    'em',      // Emphasized text\r\n    'b',       // Bold text (alternative)\r\n    'i',       // Italic text\r\n    'mark',    // Marked/highlighted text\r\n    'small',   // Small text\r\n    'del',     // Deleted text\r\n    'ins',     // Inserted text\r\n    'sub',     // Subscript\r\n    'sup'      // Superscript\r\n  ];\r\n\r\n  private htmlRemovalRegex: RegExp;\r\n\r\n  /**\r\n   * Creates a new TranscriptParser instance.\r\n   *\r\n   * @param {boolean} [preserveFormatting=false] - Whether to preserve HTML formatting tags\r\n   */\r\n  constructor(preserveFormatting: boolean = false) {\r\n    this.htmlRemovalRegex = this.buildHtmlRemovalRegex(preserveFormatting);\r\n  }\r\n\r\n  /**\r\n   * Builds a regex pattern for removing HTML tags based on formatting preferences.\r\n   *\r\n   * @private\r\n   * @param {boolean} preserveFormatting - Whether to preserve formatting tags\r\n   * @returns {RegExp} The HTML removal regex\r\n   */\r\n  private buildHtmlRemovalRegex(preserveFormatting: boolean): RegExp {\r\n    if (preserveFormatting) {\r\n      // Create a regex that removes all tags EXCEPT the formatting tags\r\n      const formattingTagsPattern = TranscriptParser.FORMATTING_TAGS.join('|');\r\n      const pattern = `<\\\\/?(?!\\\\/?(?:${formattingTagsPattern})\\\\b).*?\\\\b>`;\r\n      return new RegExp(pattern, 'gi');\r\n    } else {\r\n      // Remove all HTML tags\r\n      return /<[^>]*>/gi;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Parses raw XML transcript data into an array of transcript snippets.\r\n   *\r\n   * @param {string} xmlData - The raw XML data from YouTube's transcript API\r\n   * @returns {FetchedTranscriptSnippet[]} Array of parsed transcript snippets\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const parser = new TranscriptParser();\r\n   * const snippets = parser.parseTranscriptXml(xmlData);\r\n   * for (const snippet of snippets) {\r\n   *   console.log(`${snippet.start}s: ${snippet.text}`);\r\n   * }\r\n   * ```\r\n   */\r\n  parseTranscriptXml(xmlData: string): FetchedTranscriptSnippet[] {\r\n    const parser = new XMLParser({\r\n      ignoreAttributes: false,\r\n      attributeNamePrefix: '@_'\r\n    });\r\n\r\n    const parsedXml = parser.parse(xmlData);\r\n    const textElements = parsedXml.transcript?.text || [];\r\n\r\n    // Ensure textElements is always an array (XMLParser returns single object if only one element)\r\n    const elementsArray = Array.isArray(textElements) ? textElements : [textElements];\r\n\r\n    return elementsArray\r\n      .filter(element => element['#text'])\r\n      .map(element => this.parseTranscriptSnippet(element));\r\n  }\r\n\r\n  /**\r\n   * Parses a single XML element into a FetchedTranscriptSnippet.\r\n   *\r\n   * @private\r\n   * @param {any} element - The XML element to parse\r\n   * @returns {FetchedTranscriptSnippet} The parsed snippet\r\n   */\r\n  private parseTranscriptSnippet(element: any): FetchedTranscriptSnippet {\r\n    return {\r\n      text: this.cleanTranscriptText(element['#text']),\r\n      start: this.parseFloatAttribute(element['@_start']),\r\n      duration: this.parseFloatAttribute(element['@_dur'])\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Parses a string attribute as a float, defaulting to 0 if invalid.\r\n   *\r\n   * @private\r\n   * @param {string | undefined} value - The attribute value to parse\r\n   * @returns {number} The parsed float value\r\n   */\r\n  private parseFloatAttribute(value: string | undefined): number {\r\n    return parseFloat(value || '0');\r\n  }\r\n\r\n  /**\r\n   * Cleans transcript text by decoding HTML entities and removing HTML tags.\r\n   *\r\n   * @private\r\n   * @param {string} text - The raw transcript text\r\n   * @returns {string} The cleaned text\r\n   */\r\n  private cleanTranscriptText(text: string): string {\r\n    // Ensure text is a string\r\n    if (typeof text !== 'string') {\r\n      text = String(text || '');\r\n    }\r\n    const decodedText = decode(text);\r\n    return decodedText.replace(this.htmlRemovalRegex, '');\r\n  }\r\n}\r\n","/**\r\n * @fileoverview HTTP error handling utilities for YouTube API requests\r\n */\r\n\r\nimport { IpBlocked, YouTubeRequestFailed } from '../errors';\r\n\r\n/**\r\n * Checks an HTTP response and throws appropriate errors if the request failed.\r\n *\r\n * @param {Response} response - The HTTP response to check\r\n * @param {string} videoId - The video ID associated with the request\r\n * @throws {IpBlocked} If status is 429 (Too Many Requests)\r\n * @throws {YouTubeRequestFailed} If status indicates an error\r\n *\r\n * @example\r\n * ```typescript\r\n * const response = await httpClient.get(url);\r\n * handleHttpErrors(response, videoId);\r\n * // If we reach here, the request was successful\r\n * ```\r\n */\r\nexport function handleHttpErrors(response: Response, videoId: string): void {\r\n  if (response.status === 429) {\r\n    throw new IpBlocked(videoId);\r\n  }\r\n\r\n  if (!response.ok) {\r\n    const errorMessage = `HTTP ${response.status}: ${response.statusText}`;\r\n    throw new YouTubeRequestFailed(videoId, new Error(errorMessage));\r\n  }\r\n}\r\n","/**\r\n * @fileoverview Transcript model representing a single transcript that can be fetched or translated\r\n */\r\n\r\nimport { HttpClient, TranslationLanguage } from '../types';\r\nimport { NotTranslatable, TranslationLanguageNotAvailable, PoTokenRequired } from '../errors';\r\nimport { FetchedTranscript } from './fetched-transcript';\r\nimport { TranscriptParser } from '../parsers/transcript-parser';\r\nimport { handleHttpErrors } from '../utils/http-error-handler';\r\n\r\n/**\r\n * Represents a transcript that can be fetched or translated.\r\n * You typically obtain Transcript objects from a TranscriptList.\r\n *\r\n * @example\r\n * ```typescript\r\n * const transcriptList = await api.list('video_id');\r\n * const transcript = transcriptList.findTranscript(['en']);\r\n * const fetched = await transcript.fetch();\r\n * ```\r\n */\r\nexport class Transcript {\r\n  private httpClient: HttpClient;\r\n\r\n  /** The video ID this transcript belongs to */\r\n  public readonly videoId: string;\r\n\r\n  private transcriptUrl: string;\r\n\r\n  /** The language name (e.g., \"English\") */\r\n  public readonly language: string;\r\n\r\n  /** The language code (e.g., \"en\") */\r\n  public readonly languageCode: string;\r\n\r\n  /** Whether this transcript was automatically generated */\r\n  public readonly isGenerated: boolean;\r\n\r\n  /** Available translation languages */\r\n  public readonly translationLanguages: TranslationLanguage[];\r\n\r\n  private translationLanguagesMap: Record<string, string>;\r\n\r\n  /**\r\n   * Creates a new Transcript instance.\r\n   * You probably don't want to initialize this directly.\r\n   * Usually you'll access Transcript objects using a TranscriptList.\r\n   *\r\n   * @param {HttpClient} httpClient - HTTP client for making requests\r\n   * @param {string} videoId - The video ID\r\n   * @param {string} transcriptUrl - The transcript URL\r\n   * @param {string} language - The language name\r\n   * @param {string} languageCode - The language code\r\n   * @param {boolean} isGenerated - Whether the transcript is auto-generated\r\n   * @param {TranslationLanguage[]} translationLanguages - Available translation languages\r\n   */\r\n  constructor(\r\n    httpClient: HttpClient,\r\n    videoId: string,\r\n    transcriptUrl: string,\r\n    language: string,\r\n    languageCode: string,\r\n    isGenerated: boolean,\r\n    translationLanguages: TranslationLanguage[]\r\n  ) {\r\n    this.httpClient = httpClient;\r\n    this.videoId = videoId;\r\n    this.transcriptUrl = transcriptUrl;\r\n    this.language = language;\r\n    this.languageCode = languageCode;\r\n    this.isGenerated = isGenerated;\r\n    this.translationLanguages = translationLanguages;\r\n    this.translationLanguagesMap = this.buildTranslationLanguagesMap(translationLanguages);\r\n  }\r\n\r\n  /**\r\n   * Builds a map of language codes to language names for quick lookup.\r\n   *\r\n   * @private\r\n   * @param {TranslationLanguage[]} languages - Array of translation languages\r\n   * @returns {Record<string, string>} Map of language code to language name\r\n   */\r\n  private buildTranslationLanguagesMap(languages: TranslationLanguage[]): Record<string, string> {\r\n    const map: Record<string, string> = {};\r\n    for (const lang of languages) {\r\n      map[lang.language_code] = lang.language;\r\n    }\r\n    return map;\r\n  }\r\n\r\n  /**\r\n   * Loads the actual transcript data from YouTube.\r\n   *\r\n   * @param {boolean} [preserveFormatting=false] - Whether to keep select HTML text formatting\r\n   * @returns {Promise<FetchedTranscript>} The fetched transcript with all snippets\r\n   * @throws {PoTokenRequired} If a PO Token is required for this video\r\n   * @throws {YouTubeRequestFailed} If the HTTP request fails\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const transcript = transcriptList.findTranscript(['en']);\r\n   * const fetched = await transcript.fetch();\r\n   *\r\n   * // With formatting preserved\r\n   * const fetchedWithFormatting = await transcript.fetch(true);\r\n   * ```\r\n   */\r\n  async fetch(preserveFormatting: boolean = false): Promise<FetchedTranscript> {\r\n    if (this.transcriptUrl.includes('&exp=xpe')) {\r\n      throw new PoTokenRequired(this.videoId);\r\n    }\r\n\r\n    const response = await this.httpClient.get(this.transcriptUrl);\r\n    handleHttpErrors(response, this.videoId);\r\n\r\n    const xmlData = await response.text();\r\n    const parser = new TranscriptParser(preserveFormatting);\r\n    const snippets = parser.parseTranscriptXml(xmlData);\r\n\r\n    return new FetchedTranscript(\r\n      snippets,\r\n      this.videoId,\r\n      this.language,\r\n      this.languageCode,\r\n      this.isGenerated\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Checks whether this transcript can be translated to other languages.\r\n   *\r\n   * @returns {boolean} True if the transcript supports translation\r\n   */\r\n  get isTranslatable(): boolean {\r\n    return this.translationLanguages.length > 0;\r\n  }\r\n\r\n  /**\r\n   * Creates a translated version of this transcript.\r\n   *\r\n   * @param {string} targetLanguageCode - The target language code (e.g., 'de', 'fr')\r\n   * @returns {Transcript} A new Transcript object for the translated transcript\r\n   * @throws {NotTranslatable} If the transcript cannot be translated\r\n   * @throws {TranslationLanguageNotAvailable} If the requested language is not available\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const englishTranscript = transcriptList.findTranscript(['en']);\r\n   * if (englishTranscript.isTranslatable) {\r\n   *   const germanTranscript = englishTranscript.translate('de');\r\n   *   const fetched = await germanTranscript.fetch();\r\n   * }\r\n   * ```\r\n   */\r\n  translate(targetLanguageCode: string): Transcript {\r\n    if (!this.isTranslatable) {\r\n      throw new NotTranslatable(this.videoId);\r\n    }\r\n\r\n    if (!(targetLanguageCode in this.translationLanguagesMap)) {\r\n      throw new TranslationLanguageNotAvailable(this.videoId);\r\n    }\r\n\r\n    const translatedUrl = `${this.transcriptUrl}&tlang=${targetLanguageCode}`;\r\n    const translatedLanguage = this.translationLanguagesMap[targetLanguageCode];\r\n\r\n    return new Transcript(\r\n      this.httpClient,\r\n      this.videoId,\r\n      translatedUrl,\r\n      translatedLanguage,\r\n      targetLanguageCode,\r\n      true,\r\n      [] // Translated transcripts cannot be translated again\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Returns a string representation of the transcript.\r\n   *\r\n   * @returns {string} A formatted string representation\r\n   */\r\n  toString(): string {\r\n    const translatableFlag = this.isTranslatable ? '[TRANSLATABLE]' : '';\r\n    return `${this.languageCode} (\"${this.language}\")${translatableFlag}`;\r\n  }\r\n}\r\n","/**\r\n * @fileoverview TranscriptList model representing all available transcripts for a video\r\n */\r\n\r\nimport { HttpClient, TranslationLanguage, CaptionsJson } from '../types';\r\nimport { Transcript } from './transcript';\r\nimport { NoTranscriptFound } from '../errors';\r\n\r\n/**\r\n * Represents a list of available transcripts for a video.\r\n * This object is iterable and provides methods to search for specific transcripts.\r\n *\r\n * @example\r\n * ```typescript\r\n * const transcriptList = await api.list('video_id');\r\n *\r\n * // Iterate over all transcripts\r\n * for (const transcript of transcriptList) {\r\n *   console.log(transcript.language, transcript.languageCode);\r\n * }\r\n *\r\n * // Find a specific transcript\r\n * const transcript = transcriptList.findTranscript(['de', 'en']);\r\n * ```\r\n */\r\nexport class TranscriptList implements Iterable<Transcript> {\r\n  /** The video ID this list belongs to */\r\n  public readonly videoId: string;\r\n\r\n  private manuallyCreatedTranscripts: Record<string, Transcript>;\r\n  private autoGeneratedTranscripts: Record<string, Transcript>;\r\n  private availableTranslationLanguages: TranslationLanguage[];\r\n\r\n  /**\r\n   * Creates a new TranscriptList instance.\r\n   * The constructor is only for internal use. Use the static buildFromCaptionsJson method instead.\r\n   *\r\n   * @param {string} videoId - The video ID\r\n   * @param {Record<string, Transcript>} manuallyCreatedTranscripts - Map of manually created transcripts\r\n   * @param {Record<string, Transcript>} autoGeneratedTranscripts - Map of auto-generated transcripts\r\n   * @param {TranslationLanguage[]} availableTranslationLanguages - Available translation languages\r\n   */\r\n  constructor(\r\n    videoId: string,\r\n    manuallyCreatedTranscripts: Record<string, Transcript>,\r\n    autoGeneratedTranscripts: Record<string, Transcript>,\r\n    availableTranslationLanguages: TranslationLanguage[]\r\n  ) {\r\n    this.videoId = videoId;\r\n    this.manuallyCreatedTranscripts = manuallyCreatedTranscripts;\r\n    this.autoGeneratedTranscripts = autoGeneratedTranscripts;\r\n    this.availableTranslationLanguages = availableTranslationLanguages;\r\n  }\r\n\r\n  /**\r\n   * Factory method to build a TranscriptList from YouTube's captions JSON data.\r\n   *\r\n   * @param {HttpClient} httpClient - HTTP client for making requests\r\n   * @param {string} videoId - The video ID\r\n   * @param {CaptionsJson} captionsJson - The JSON parsed from YouTube's response\r\n   * @returns {TranscriptList} The created TranscriptList\r\n   */\r\n  static buildFromCaptionsJson(\r\n    httpClient: HttpClient,\r\n    videoId: string,\r\n    captionsJson: CaptionsJson\r\n  ): TranscriptList {\r\n    const translationLanguages = TranscriptList.extractTranslationLanguages(captionsJson);\r\n    const { manualTranscripts, generatedTranscripts } = TranscriptList.categorizeTranscripts(\r\n      httpClient,\r\n      videoId,\r\n      captionsJson,\r\n      translationLanguages\r\n    );\r\n\r\n    return new TranscriptList(videoId, manualTranscripts, generatedTranscripts, translationLanguages);\r\n  }\r\n\r\n  /**\r\n   * Extracts translation languages from the captions JSON.\r\n   *\r\n   * @private\r\n   * @param {CaptionsJson} captionsJson - The captions JSON data\r\n   * @returns {TranslationLanguage[]} Array of translation languages\r\n   */\r\n  private static extractTranslationLanguages(captionsJson: CaptionsJson): TranslationLanguage[] {\r\n    return (captionsJson.translationLanguages || []).map(lang => ({\r\n      language: lang.languageName.runs[0].text,\r\n      language_code: lang.languageCode\r\n    }));\r\n  }\r\n\r\n  /**\r\n   * Categorizes transcripts into manually created and auto-generated.\r\n   *\r\n   * @private\r\n   * @param {HttpClient} httpClient - HTTP client for making requests\r\n   * @param {string} videoId - The video ID\r\n   * @param {CaptionsJson} captionsJson - The captions JSON data\r\n   * @param {TranslationLanguage[]} translationLanguages - Available translation languages\r\n   * @returns {{manualTranscripts: Record<string, Transcript>, generatedTranscripts: Record<string, Transcript>}}\r\n   */\r\n  private static categorizeTranscripts(\r\n    httpClient: HttpClient,\r\n    videoId: string,\r\n    captionsJson: CaptionsJson,\r\n    translationLanguages: TranslationLanguage[]\r\n  ): {\r\n    manualTranscripts: Record<string, Transcript>;\r\n    generatedTranscripts: Record<string, Transcript>;\r\n  } {\r\n    const manualTranscripts: Record<string, Transcript> = {};\r\n    const generatedTranscripts: Record<string, Transcript> = {};\r\n\r\n    for (const captionTrack of captionsJson.captionTracks) {\r\n      const isAutoGenerated = captionTrack.kind === 'asr';\r\n      const targetMap = isAutoGenerated ? generatedTranscripts : manualTranscripts;\r\n\r\n      // Remove format parameter from URL for better compatibility\r\n      const cleanedUrl = captionTrack.baseUrl.replace('&fmt=srv3', '');\r\n\r\n      const transcript = new Transcript(\r\n        httpClient,\r\n        videoId,\r\n        cleanedUrl,\r\n        captionTrack.name.runs[0].text,\r\n        captionTrack.languageCode,\r\n        isAutoGenerated,\r\n        captionTrack.isTranslatable ? translationLanguages : []\r\n      );\r\n\r\n      targetMap[captionTrack.languageCode] = transcript;\r\n    }\r\n\r\n    return { manualTranscripts, generatedTranscripts };\r\n  }\r\n\r\n  /**\r\n   * Makes the transcript list iterable, allowing for...of loops.\r\n   * Manual transcripts are yielded first, followed by auto-generated transcripts.\r\n   *\r\n   * @returns {Iterator<Transcript>} Iterator over all transcripts\r\n   */\r\n  [Symbol.iterator](): Iterator<Transcript> {\r\n    const manualTranscripts = Object.values(this.manuallyCreatedTranscripts);\r\n    const generatedTranscripts = Object.values(this.autoGeneratedTranscripts);\r\n    return [...manualTranscripts, ...generatedTranscripts][Symbol.iterator]();\r\n  }\r\n\r\n  /**\r\n   * Finds a transcript for given language codes. Manually created transcripts are\r\n   * returned first and only if none are found, auto-generated transcripts are used.\r\n   *\r\n   * @param {string[]} languageCodes - A list of language codes in descending priority\r\n   * @returns {Transcript} The found transcript\r\n   * @throws {NoTranscriptFound} If no transcript is found for any of the language codes\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * // Try German first, then English\r\n   * const transcript = transcriptList.findTranscript(['de', 'en']);\r\n   * ```\r\n   */\r\n  findTranscript(languageCodes: string[]): Transcript {\r\n    return this.findTranscriptInMaps(languageCodes, [\r\n      this.manuallyCreatedTranscripts,\r\n      this.autoGeneratedTranscripts\r\n    ]);\r\n  }\r\n\r\n  /**\r\n   * Finds an automatically generated transcript for given language codes.\r\n   *\r\n   * @param {string[]} languageCodes - A list of language codes in descending priority\r\n   * @returns {Transcript} The found auto-generated transcript\r\n   * @throws {NoTranscriptFound} If no auto-generated transcript is found\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const transcript = transcriptList.findGeneratedTranscript(['en']);\r\n   * ```\r\n   */\r\n  findGeneratedTranscript(languageCodes: string[]): Transcript {\r\n    return this.findTranscriptInMaps(languageCodes, [this.autoGeneratedTranscripts]);\r\n  }\r\n\r\n  /**\r\n   * Finds a manually created transcript for given language codes.\r\n   *\r\n   * @param {string[]} languageCodes - A list of language codes in descending priority\r\n   * @returns {Transcript} The found manually created transcript\r\n   * @throws {NoTranscriptFound} If no manually created transcript is found\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const transcript = transcriptList.findManuallyCreatedTranscript(['en']);\r\n   * ```\r\n   */\r\n  findManuallyCreatedTranscript(languageCodes: string[]): Transcript {\r\n    return this.findTranscriptInMaps(languageCodes, [this.manuallyCreatedTranscripts]);\r\n  }\r\n\r\n  /**\r\n   * Internal method to find a transcript by searching through provided maps.\r\n   *\r\n   * @private\r\n   * @param {string[]} languageCodes - Language codes to search for\r\n   * @param {Record<string, Transcript>[]} transcriptMaps - Maps to search through\r\n   * @returns {Transcript} The found transcript\r\n   * @throws {NoTranscriptFound} If no transcript is found\r\n   */\r\n  private findTranscriptInMaps(\r\n    languageCodes: string[],\r\n    transcriptMaps: Record<string, Transcript>[]\r\n  ): Transcript {\r\n    for (const languageCode of languageCodes) {\r\n      for (const transcriptMap of transcriptMaps) {\r\n        if (languageCode in transcriptMap) {\r\n          return transcriptMap[languageCode];\r\n        }\r\n      }\r\n    }\r\n\r\n    throw new NoTranscriptFound(this.videoId, languageCodes, this);\r\n  }\r\n\r\n  /**\r\n   * Returns a formatted string representation of all available transcripts.\r\n   *\r\n   * @returns {string} A formatted string showing all available transcripts\r\n   */\r\n  toString(): string {\r\n    const formatTranscriptMap = (transcripts: Record<string, Transcript>): string => {\r\n      const descriptions = Object.values(transcripts).map(t => ` - ${t.toString()}`);\r\n      return descriptions.length > 0 ? descriptions.join('\\n') : 'None';\r\n    };\r\n\r\n    const formatTranslationLanguages = (): string => {\r\n      const descriptions = this.availableTranslationLanguages.map(\r\n        lang => ` - ${lang.language_code} (\"${lang.language}\")`\r\n      );\r\n      return descriptions.length > 0 ? descriptions.join('\\n') : 'None';\r\n    };\r\n\r\n    return (\r\n      `For this video (${this.videoId}) transcripts are available in the following languages:\\n\\n` +\r\n      `(MANUALLY CREATED)\\n${formatTranscriptMap(this.manuallyCreatedTranscripts)}\\n\\n` +\r\n      `(GENERATED)\\n${formatTranscriptMap(this.autoGeneratedTranscripts)}\\n\\n` +\r\n      `(TRANSLATION LANGUAGES)\\n${formatTranslationLanguages()}`\r\n    );\r\n  }\r\n}\r\n","/**\r\n * @fileoverview Type definitions for the YouTube Transcript API\r\n */\r\n\r\n/**\r\n * Represents a single snippet/cue of a transcript\r\n */\r\nexport interface FetchedTranscriptSnippet {\r\n  /** The text content of this transcript snippet */\r\n  text: string;\r\n  /** The timestamp at which this transcript snippet appears on screen in seconds */\r\n  start: number;\r\n  /**\r\n   * The duration of how long the snippet stays on screen in seconds.\r\n   * Note: This is not the duration of the transcribed speech, but how long\r\n   * the snippet stays on screen. Therefore, there can be overlaps between snippets!\r\n   */\r\n  duration: number;\r\n}\r\n\r\n/**\r\n * Translation language information\r\n */\r\nexport interface TranslationLanguage {\r\n  /** The name of the language */\r\n  language: string;\r\n  /** The language code (e.g., 'en', 'de', 'fr') */\r\n  language_code: string;\r\n}\r\n\r\n/**\r\n * Playability status enum values\r\n */\r\nexport enum PlayabilityStatus {\r\n  OK = 'OK',\r\n  ERROR = 'ERROR',\r\n  LOGIN_REQUIRED = 'LOGIN_REQUIRED'\r\n}\r\n\r\n/**\r\n * Playability failed reason enum values\r\n */\r\nexport enum PlayabilityFailedReason {\r\n  BOT_DETECTED = \"Sign in to confirm you're not a bot\",\r\n  AGE_RESTRICTED = 'This video may be inappropriate for some users.',\r\n  VIDEO_UNAVAILABLE = 'This video is unavailable'\r\n}\r\n\r\n/**\r\n * Proxy configuration dictionary for HTTP clients\r\n */\r\nexport interface RequestsProxyConfigDict {\r\n  http: string;\r\n  https: string;\r\n}\r\n\r\n/**\r\n * Caption track data from YouTube API\r\n */\r\nexport interface CaptionTrack {\r\n  baseUrl: string;\r\n  name: {\r\n    runs: Array<{ text: string }>;\r\n  };\r\n  languageCode: string;\r\n  kind?: string;\r\n  isTranslatable?: boolean;\r\n}\r\n\r\n/**\r\n * Captions JSON data from YouTube\r\n */\r\nexport interface CaptionsJson {\r\n  captionTracks: CaptionTrack[];\r\n  translationLanguages?: Array<{\r\n    languageName: {\r\n      runs: Array<{ text: string }>;\r\n    };\r\n    languageCode: string;\r\n  }>;\r\n}\r\n\r\n/**\r\n * YouTube InnerTube API response\r\n */\r\nexport interface InnerTubeData {\r\n  playabilityStatus?: {\r\n    status?: string;\r\n    reason?: string;\r\n    errorScreen?: {\r\n      playerErrorMessageRenderer?: {\r\n        subreason?: {\r\n          runs?: Array<{ text?: string }>;\r\n        };\r\n      };\r\n    };\r\n  };\r\n  captions?: {\r\n    playerCaptionsTracklistRenderer?: CaptionsJson;\r\n  };\r\n}\r\n\r\n/**\r\n * HTTP client interface with fetch-like API\r\n */\r\nexport interface HttpClient {\r\n  get(url: string, options?: RequestInit): Promise<Response>;\r\n  post(url: string, options?: RequestInit): Promise<Response>;\r\n}\r\n\r\n/**\r\n * Cookie storage interface\r\n */\r\nexport interface CookieJar {\r\n  set(name: string, value: string, domain: string): void;\r\n  get(name: string): string | undefined;\r\n}\r\n","/**\r\n * @fileoverview Utilities for extracting data from YouTube HTML and InnerTube API responses\r\n */\r\n\r\nimport {\r\n  InnerTubeData,\r\n  CaptionsJson,\r\n  PlayabilityStatus,\r\n  PlayabilityFailedReason\r\n} from '../types';\r\nimport {\r\n  IpBlocked,\r\n  YouTubeDataUnparsable,\r\n  TranscriptsDisabled,\r\n  RequestBlocked,\r\n  AgeRestricted,\r\n  VideoUnavailable,\r\n  InvalidVideoId,\r\n  VideoUnplayable\r\n} from '../errors';\r\n\r\n/**\r\n * Extracts the InnerTube API key from YouTube's HTML page.\r\n *\r\n * @param {string} htmlContent - The HTML content from YouTube's watch page\r\n * @param {string} videoId - The video ID being processed\r\n * @returns {string} The extracted API key\r\n * @throws {IpBlocked} If the page contains a reCAPTCHA (IP is blocked)\r\n * @throws {YouTubeDataUnparsable} If the API key cannot be found\r\n *\r\n * @example\r\n * ```typescript\r\n * const html = await fetchVideoHtml(videoId);\r\n * const apiKey = extractInnertubeApiKey(html, videoId);\r\n * ```\r\n */\r\nexport function extractInnertubeApiKey(htmlContent: string, videoId: string): string {\r\n  const apiKeyPattern = /\"INNERTUBE_API_KEY\":\\s*\"([a-zA-Z0-9_-]+)\"/;\r\n  const match = htmlContent.match(apiKeyPattern);\r\n\r\n  if (match && match.length === 2) {\r\n    return match[1];\r\n  }\r\n\r\n  // Check if IP is blocked (reCAPTCHA present)\r\n  if (htmlContent.includes('class=\"g-recaptcha\"')) {\r\n    throw new IpBlocked(videoId);\r\n  }\r\n\r\n  throw new YouTubeDataUnparsable(videoId);\r\n}\r\n\r\n/**\r\n * Extracts captions JSON from YouTube's InnerTube API response.\r\n *\r\n * @param {InnerTubeData} innertubeData - The InnerTube API response data\r\n * @param {string} videoId - The video ID being processed\r\n * @returns {CaptionsJson} The extracted captions data\r\n * @throws {TranscriptsDisabled} If captions are disabled for the video\r\n *\r\n * @example\r\n * ```typescript\r\n * const innertubeData = await fetchInnertubeData(videoId, apiKey);\r\n * const captionsJson = extractCaptionsJson(innertubeData, videoId);\r\n * ```\r\n */\r\nexport function extractCaptionsJson(innertubeData: InnerTubeData, videoId: string): CaptionsJson {\r\n  validatePlayabilityStatus(innertubeData.playabilityStatus, videoId);\r\n\r\n  const captionsJson = innertubeData.captions?.playerCaptionsTracklistRenderer;\r\n\r\n  if (!captionsJson || !captionsJson.captionTracks) {\r\n    throw new TranscriptsDisabled(videoId);\r\n  }\r\n\r\n  return captionsJson;\r\n}\r\n\r\n/**\r\n * Validates the playability status of a video and throws appropriate errors.\r\n *\r\n * @param {any} playabilityStatusData - The playability status data from InnerTube\r\n * @param {string} videoId - The video ID being validated\r\n * @throws {RequestBlocked} If YouTube detected a bot\r\n * @throws {AgeRestricted} If the video is age-restricted\r\n * @throws {VideoUnavailable} If the video is unavailable\r\n * @throws {InvalidVideoId} If the video ID is invalid (URL was provided instead)\r\n * @throws {VideoUnplayable} If the video is unplayable for any other reason\r\n *\r\n * @example\r\n * ```typescript\r\n * validatePlayabilityStatus(innertubeData.playabilityStatus, videoId);\r\n * ```\r\n */\r\nexport function validatePlayabilityStatus(playabilityStatusData: any, videoId: string): void {\r\n  if (!playabilityStatusData) {\r\n    return;\r\n  }\r\n\r\n  const playabilityStatus = playabilityStatusData.status;\r\n\r\n  // If status is OK, video is playable\r\n  if (playabilityStatus === PlayabilityStatus.OK || !playabilityStatus) {\r\n    return;\r\n  }\r\n\r\n  const reason = playabilityStatusData.reason;\r\n\r\n  // Handle LOGIN_REQUIRED status\r\n  if (playabilityStatus === PlayabilityStatus.LOGIN_REQUIRED) {\r\n    if (reason === PlayabilityFailedReason.BOT_DETECTED) {\r\n      throw new RequestBlocked(videoId);\r\n    }\r\n    if (reason === PlayabilityFailedReason.AGE_RESTRICTED) {\r\n      throw new AgeRestricted(videoId);\r\n    }\r\n  }\r\n\r\n  // Handle ERROR status\r\n  if (playabilityStatus === PlayabilityStatus.ERROR && reason === PlayabilityFailedReason.VIDEO_UNAVAILABLE) {\r\n    // Check if user provided a URL instead of video ID\r\n    if (videoId.startsWith('http://') || videoId.startsWith('https://')) {\r\n      throw new InvalidVideoId(videoId);\r\n    }\r\n    throw new VideoUnavailable(videoId);\r\n  }\r\n\r\n  // Extract additional sub-reasons if available\r\n  const subReasons = extractPlayabilitySubreasons(playabilityStatusData);\r\n\r\n  throw new VideoUnplayable(videoId, reason, subReasons);\r\n}\r\n\r\n/**\r\n * Extracts sub-reasons from the playability status error screen.\r\n *\r\n * @private\r\n * @param {any} playabilityStatusData - The playability status data\r\n * @returns {string[]} Array of sub-reason messages\r\n */\r\nfunction extractPlayabilitySubreasons(playabilityStatusData: any): string[] {\r\n  const runs = playabilityStatusData.errorScreen?.playerErrorMessageRenderer?.subreason?.runs;\r\n\r\n  if (!runs || !Array.isArray(runs)) {\r\n    return [];\r\n  }\r\n\r\n  return runs\r\n    .map((run: any) => run.text || '')\r\n    .filter((text: string) => text.length > 0);\r\n}\r\n","/**\r\n * @fileoverview Fetcher for retrieving transcript lists from YouTube\r\n */\r\n\r\nimport { decode } from 'html-entities';\r\nimport { HttpClient, CaptionsJson, InnerTubeData } from '../types';\r\nimport { ProxyConfig } from '../proxies';\r\nimport { TranscriptList } from '../models/transcript-list';\r\nimport { RequestBlocked, FailedToCreateConsentCookie } from '../errors';\r\nimport { handleHttpErrors } from '../utils/http-error-handler';\r\nimport {\r\n  extractInnertubeApiKey,\r\n  extractCaptionsJson\r\n} from './youtube-data-extractor';\r\n\r\n/** YouTube watch page URL template */\r\nconst YOUTUBE_WATCH_URL = 'https://www.youtube.com/watch?v={video_id}';\r\n\r\n/** YouTube InnerTube API URL template */\r\nconst INNERTUBE_API_URL = 'https://www.youtube.com/youtubei/v1/player?key={api_key}';\r\n\r\n/** InnerTube API context configuration for requests */\r\nconst INNERTUBE_CONTEXT = {\r\n  client: {\r\n    clientName: 'ANDROID',\r\n    clientVersion: '20.10.38'\r\n  }\r\n};\r\n\r\n/**\r\n * Fetches transcript lists from YouTube for given video IDs.\r\n * Handles consent cookies, retries on blocks, and extracts caption data.\r\n *\r\n * @example\r\n * ```typescript\r\n * const fetcher = new TranscriptListFetcher(httpClient, proxyConfig);\r\n * const transcriptList = await fetcher.fetchTranscriptList('dQw4w9WgXcQ');\r\n * ```\r\n */\r\nexport class TranscriptListFetcher {\r\n  private httpClient: HttpClient;\r\n  private proxyConfig: ProxyConfig | null;\r\n\r\n  /**\r\n   * Creates a new TranscriptListFetcher instance.\r\n   *\r\n   * @param {HttpClient} httpClient - The HTTP client to use for requests\r\n   * @param {ProxyConfig | null} proxyConfig - Optional proxy configuration\r\n   */\r\n  constructor(httpClient: HttpClient, proxyConfig: ProxyConfig | null) {\r\n    this.httpClient = httpClient;\r\n    this.proxyConfig = proxyConfig;\r\n  }\r\n\r\n  /**\r\n   * Fetches the list of available transcripts for a video.\r\n   *\r\n   * @param {string} videoId - The YouTube video ID\r\n   * @returns {Promise<TranscriptList>} The transcript list\r\n   * @throws {RequestBlocked} If requests are blocked and retries exhausted\r\n   * @throws {IpBlocked} If IP is blocked by YouTube\r\n   * @throws {YouTubeDataUnparsable} If YouTube data cannot be parsed\r\n   * @throws {TranscriptsDisabled} If transcripts are disabled for the video\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const transcriptList = await fetcher.fetchTranscriptList('dQw4w9WgXcQ');\r\n   * const transcript = transcriptList.findTranscript(['en']);\r\n   * ```\r\n   */\r\n  async fetchTranscriptList(videoId: string): Promise<TranscriptList> {\r\n    const captionsJson = await this.fetchCaptionsJsonWithRetry(videoId);\r\n    return TranscriptList.buildFromCaptionsJson(this.httpClient, videoId, captionsJson);\r\n  }\r\n\r\n  /**\r\n   * Fetches captions JSON with retry logic for blocked requests.\r\n   *\r\n   * @private\r\n   * @param {string} videoId - The video ID\r\n   * @param {number} [attemptNumber=0] - Current attempt number (for retry logic)\r\n   * @returns {Promise<CaptionsJson>} The captions JSON data\r\n   */\r\n  private async fetchCaptionsJsonWithRetry(\r\n    videoId: string,\r\n    attemptNumber: number = 0\r\n  ): Promise<CaptionsJson> {\r\n    try {\r\n      const htmlContent = await this.fetchVideoPageHtml(videoId);\r\n      const apiKey = extractInnertubeApiKey(htmlContent, videoId);\r\n      const innertubeData = await this.fetchInnertubePlayerData(videoId, apiKey);\r\n      return extractCaptionsJson(innertubeData, videoId);\r\n    } catch (error) {\r\n      if (error instanceof RequestBlocked) {\r\n        return this.handleBlockedRequest(error, videoId, attemptNumber);\r\n      }\r\n      throw error;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Handles blocked requests with retry logic based on proxy configuration.\r\n   *\r\n   * @private\r\n   * @param {RequestBlocked} error - The blocked request error\r\n   * @param {string} videoId - The video ID\r\n   * @param {number} attemptNumber - Current attempt number\r\n   * @returns {Promise<CaptionsJson>} The captions JSON data from retry\r\n   * @throws {RequestBlocked} If retries are exhausted\r\n   */\r\n  private async handleBlockedRequest(\r\n    error: RequestBlocked,\r\n    videoId: string,\r\n    attemptNumber: number\r\n  ): Promise<CaptionsJson> {\r\n    const maxRetries = this.proxyConfig?.retriesWhenBlocked || 0;\r\n\r\n    if (attemptNumber + 1 < maxRetries) {\r\n      return this.fetchCaptionsJsonWithRetry(videoId, attemptNumber + 1);\r\n    }\r\n\r\n    throw error.withProxyConfig(this.proxyConfig);\r\n  }\r\n\r\n  /**\r\n   * Fetches the HTML content of a video's watch page, handling consent cookies.\r\n   *\r\n   * @private\r\n   * @param {string} videoId - The video ID\r\n   * @returns {Promise<string>} The HTML content\r\n   * @throws {FailedToCreateConsentCookie} If consent cookie creation fails\r\n   */\r\n  private async fetchVideoPageHtml(videoId: string): Promise<string> {\r\n    let htmlContent = await this.fetchAndDecodeHtml(videoId);\r\n\r\n    // Handle YouTube consent cookie requirement\r\n    if (this.isConsentPageDetected(htmlContent)) {\r\n      this.createConsentCookie(htmlContent, videoId);\r\n      htmlContent = await this.fetchAndDecodeHtml(videoId);\r\n\r\n      if (this.isConsentPageDetected(htmlContent)) {\r\n        throw new FailedToCreateConsentCookie(videoId);\r\n      }\r\n    }\r\n\r\n    return htmlContent;\r\n  }\r\n\r\n  /**\r\n   * Checks if the HTML content is a consent page.\r\n   *\r\n   * @private\r\n   * @param {string} htmlContent - The HTML content to check\r\n   * @returns {boolean} True if consent page detected\r\n   */\r\n  private isConsentPageDetected(htmlContent: string): boolean {\r\n    return htmlContent.includes('action=\"https://consent.youtube.com/s\"');\r\n  }\r\n\r\n  /**\r\n   * Fetches HTML from YouTube watch page and decodes HTML entities.\r\n   *\r\n   * @private\r\n   * @param {string} videoId - The video ID\r\n   * @returns {Promise<string>} The decoded HTML content\r\n   */\r\n  private async fetchAndDecodeHtml(videoId: string): Promise<string> {\r\n    const url = this.buildWatchUrl(videoId);\r\n    const response = await this.httpClient.get(url);\r\n    handleHttpErrors(response, videoId);\r\n    const rawHtml = await response.text();\r\n    return decode(rawHtml);\r\n  }\r\n\r\n  /**\r\n   * Builds the YouTube watch URL for a video ID.\r\n   *\r\n   * @private\r\n   * @param {string} videoId - The video ID\r\n   * @returns {string} The watch URL\r\n   */\r\n  private buildWatchUrl(videoId: string): string {\r\n    return YOUTUBE_WATCH_URL.replace('{video_id}', videoId);\r\n  }\r\n\r\n  /**\r\n   * Creates a consent cookie from the HTML content.\r\n   * Note: This is a placeholder - cookie handling should be implemented in the HTTP client.\r\n   *\r\n   * @private\r\n   * @param {string} htmlContent - The HTML content containing consent form\r\n   * @param {string} videoId - The video ID\r\n   * @throws {FailedToCreateConsentCookie} If consent value cannot be extracted\r\n   */\r\n  private createConsentCookie(htmlContent: string, videoId: string): void {\r\n    const consentValuePattern = /name=\"v\" value=\"(.*?)\"/;\r\n    const match = htmlContent.match(consentValuePattern);\r\n\r\n    if (!match) {\r\n      throw new FailedToCreateConsentCookie(videoId);\r\n    }\r\n\r\n    // TODO: Cookie handling would need to be implemented in the HTTP client\r\n    // This is a placeholder for the cookie setting logic\r\n  }\r\n\r\n  /**\r\n   * Fetches player data from YouTube's InnerTube API.\r\n   *\r\n   * @private\r\n   * @param {string} videoId - The video ID\r\n   * @param {string} apiKey - The InnerTube API key\r\n   * @returns {Promise<InnerTubeData>} The InnerTube player data\r\n   */\r\n  private async fetchInnertubePlayerData(videoId: string, apiKey: string): Promise<InnerTubeData> {\r\n    const url = this.buildInnertubeApiUrl(apiKey);\r\n    const requestBody = this.buildInnertubeRequestBody(videoId);\r\n\r\n    const response = await this.httpClient.post(url, {\r\n      headers: { 'Content-Type': 'application/json' },\r\n      body: JSON.stringify(requestBody)\r\n    });\r\n\r\n    handleHttpErrors(response, videoId);\r\n    return (await response.json()) as InnerTubeData;\r\n  }\r\n\r\n  /**\r\n   * Builds the InnerTube API URL with the provided API key.\r\n   *\r\n   * @private\r\n   * @param {string} apiKey - The API key\r\n   * @returns {string} The complete API URL\r\n   */\r\n  private buildInnertubeApiUrl(apiKey: string): string {\r\n    return INNERTUBE_API_URL.replace('{api_key}', apiKey);\r\n  }\r\n\r\n  /**\r\n   * Builds the request body for InnerTube API calls.\r\n   *\r\n   * @private\r\n   * @param {string} videoId - The video ID\r\n   * @returns {object} The request body\r\n   */\r\n  private buildInnertubeRequestBody(videoId: string): object {\r\n    return {\r\n      context: INNERTUBE_CONTEXT,\r\n      videoId: videoId\r\n    };\r\n  }\r\n}\r\n","var e=/* @__PURE__ */(e=>(e.RESET=\"reset\",e.BLACK=\"black\",e.RED=\"red\",e.GREEN=\"green\",e.YELLOW=\"yellow\",e.BLUE=\"blue\",e.MAGENTA=\"magenta\",e.CYAN=\"cyan\",e.WHITE=\"white\",e.GRAY=\"gray\",e.BRIGHT_RED=\"brightRed\",e.BRIGHT_GREEN=\"brightGreen\",e.BRIGHT_YELLOW=\"brightYellow\",e.BRIGHT_BLUE=\"brightBlue\",e.BRIGHT_MAGENTA=\"brightMagenta\",e.BRIGHT_CYAN=\"brightCyan\",e.BRIGHT_WHITE=\"brightWhite\",e.BG_RED=\"bgRed\",e.BG_GREEN=\"bgGreen\",e.BG_YELLOW=\"bgYellow\",e.BG_BLUE=\"bgBlue\",e.BG_MAGENTA=\"bgMagenta\",e.BG_CYAN=\"bgCyan\",e.BG_WHITE=\"bgWhite\",e.BG_GRAY=\"bgGray\",e.BG_BLACK=\"bgBlack\",e.BG_BRIGHT_RED=\"bgBrightRed\",e.BG_BRIGHT_GREEN=\"bgBrightGreen\",e.BG_BRIGHT_YELLOW=\"bgBrightYellow\",e.BG_BRIGHT_BLUE=\"bgBrightBlue\",e.BG_BRIGHT_MAGENTA=\"bgBrightMagenta\",e.BG_BRIGHT_CYAN=\"bgBrightCyan\",e.BG_BRIGHT_WHITE=\"bgBrightWhite\",e))(e||{});const t={reset:[0,\"000000\"],black:[30,\"1f2937\"],red:[31,\"dc2626\"],green:[32,\"2d6a4f\"],yellow:[33,\"92400e\"],blue:[34,\"1a56db\"],magenta:[35,\"6d28d9\"],cyan:[36,\"0e7490\"],white:[37,\"374151\"],gray:[90,\"6b7280\"],brightRed:[91,\"ef4444\"],brightGreen:[92,\"059669\"],brightYellow:[93,\"d97706\"],brightBlue:[94,\"3b82f6\"],brightMagenta:[95,\"8b5cf6\"],brightCyan:[96,\"06b6d4\"],brightWhite:[97,\"f9fafb\"],bgBlack:[40,\"111827\"],bgRed:[41,\"fca5a5\"],bgGreen:[42,\"a7f3d0\"],bgYellow:[43,\"fde68a\"],bgBlue:[44,\"bfdbfe\"],bgMagenta:[45,\"ddd6fe\"],bgCyan:[46,\"a5f3fc\"],bgWhite:[47,\"f9fafb\"],bgGray:[100,\"d1d5db\"],bgBrightRed:[101,\"fee2e2\"],bgBrightGreen:[102,\"d1fae5\"],bgBrightYellow:[103,\"fef3c7\"],bgBrightBlue:[104,\"dbeafe\"],bgBrightMagenta:[105,\"ede9fe\"],bgBrightCyan:[106,\"cffafe\"],bgBrightWhite:[107,\"ffffff\"]};function r(e=\"ansi\"){const r={};for(const[n,[o,i]]of Object.entries(t))r[n]=\"html\"===e?\"reset\"===n?\"</span>\":`<span style=\"color:#${i}\">`:\"\u001b[\"+o+\"m\";return r}function n(e,t=\"ansi\"){const n=r(t);return\"string\"==typeof e?n.yellow:\"number\"==typeof e?n.cyan:\"boolean\"==typeof e?n.magenta:\"function\"==typeof e?n.red:null===e?n.gray:Array.isArray(e)?n.blue:\"object\"==typeof e?n.green:n.white}function o(e){return\"string\"==typeof e?'\"\"':\"number\"==typeof e?\"number\":\"boolean\"==typeof e?\"bool\":\"function\"==typeof e?\"function\":null===e?\"null\":Array.isArray(e)?e.length?\"[\"+o(e[0])+\"]\":\"[]\":\"object\"==typeof e?\"{...}\":typeof e}function i(e,t=0,l=\"ansi\"){const g=r(l),a=\"html\"===l,s=\"  \".repeat(t);if(\"object\"!=typeof e||null===e){return n(e,l)+o(e)+g.reset}if(Array.isArray(e)){if(a){let r=[];e.length&&(r=e.every(t=>typeof t==typeof e[0])?[i(e[0],t+1,l)]:e.map(e=>i(e,t+1,l)));return`<details open><summary style=\"cursor:pointer;display:inline\">${`${g.blue}[${g.reset}&thinsp;${e.length} × ${o(e[0]??void 0)}&thinsp;${g.blue}]${g.reset}`}</summary>${r.map(e=>`<div style=\"margin-left:1.5em\">${e}</div>`).join(\"\")}</details>`}let r=g.blue+\"[\"+g.reset;return e.length&&(r+=\"\\n\"),e.every(t=>typeof t==typeof e[0])?r+=s+\"  \"+i(e[0],t+1,l)+\",\\n\":e.forEach((n,o)=>{r+=s+\"  \"+i(n,t+1,l),o<e.length-1&&(r+=\",\"),r+=\"\\n\"}),r+=s+g.blue+\"]\"+g.reset,r}const b=Object.keys(e);if(a){const r=b.slice(0,4).join(\", \")+(b.length>4?\"…\":\"\");return`<details open><summary style=\"cursor:pointer\">${`${g.green}{${g.reset}&nbsp;${r}&nbsp;${g.green}}${g.reset}`}</summary>${b.map(r=>{const a=e[r],s=n(a,l);return\"object\"==typeof a&&null!==a?`<div style=\"margin-left:1.5em\">${s}${r}${g.reset}: ${i(a,t+1,l)}</div>`:`<div style=\"margin-left:1.5em\">${s}${r}: ${o(a)}${g.reset}</div>`}).join(\"\")}</details>`}let f=g.green+\"{\"+g.reset;return b.length&&(f+=\"\\n\"),b.forEach((r,a)=>{const y=e[r],c=n(y,l);f+=s+\"  \",f+=\"object\"==typeof y&&null!==y?c+r+g.reset+\": \"+i(y,t+1,l):c+r+\": \"+o(y)+g.reset,a<b.length-1&&(f+=\",\"),f+=\"\\n\"}),f+=s+g.green+\"}\"+g.reset,f}function l(e=\"\",t={}){let{color:n,style:o=\"color:rgb(54, 165, 220); font-size: 10pt;\",hideInProduction:l,startSpinner:g=!1,stopSpinner:a=!1}=t;const s=r();void 0===l&&(l=\"undefined\"!=typeof window&&window?.location.hostname.includes(\"localhost\")),\"object\"==typeof e&&(e=i(e)+\"\\n\\n\"+JSON.stringify(e,null,2)),Array.isArray(n)&&1==n.length&&(n=n[0]),n&&\"undefined\"!=typeof process&&(\"string\"==typeof e&&e.includes(\"%c\")&&Array.isArray(n)?e=e.replace(/%c/g,(e,t)=>s[n[t]]||\"\"):n&&\"string\"==typeof n&&(e=(s[n]||\"\")+e+s.reset));var b=0;return g?globalThis.interval=setInterval(()=>{process.stdout.write((Array.isArray(n)?s[n[0]]:s[n]||\"\")+\"\\r\"+\"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\".split(\"\")[b=++b%10]+\" \"+e+s.reset)},50):a?(clearInterval(globalThis.interval),process.stdout.write(\"\\r\"+(e||\" \")+\" \".repeat(\"string\"==typeof e?e.length+20:40)+\"\\n\")):\"string\"==typeof o?(1==o.split(\" \").length||n?o=`color: ${n||o}; font-size: 11pt;`:o.match(/^#[0-9a-fA-F]{6}$/)&&(o=`color: ${o}; font-size: 11pt;`),l?console.debug((o?\"%c\":\"\")+(e||\"\"),o):console.log((o?\"%c\":\"\")+(e||\"\"),o)):\"object\"==typeof o&&console.log(e,...o),!0}export{e as ColorName,r as getColors,l as log,i as printJSONStructure};\n//# sourceMappingURL=log.es.js.map\n","import{printJSONStructure as e,log as t}from\"./log.es.js\";const n=async(e,t)=>{let n;return async function(...o){clearTimeout(n),n=setTimeout(async()=>{clearTimeout(n),await e(...o)},t)}},o=e=>new Promise(t=>setTimeout(t,1e3*e||0)),r=(e,t)=>{let n=e=>t?.startsWith(e)||!1,o=e,r=t;return t?.startsWith(\"http:\")||t?.startsWith(\"https:\")?o=\"\":n(\"/\")||o.endsWith(\"/\")?n(\"/\")&&o.endsWith(\"/\")&&(r=t.slice(1)):r=\"/\"+t,{baseURL:o,path:r}};function s(e){if(\"undefined\"==typeof document)return;let t,n=document.getElementById(\"alert-overlay\");if(!n){n=document.body.appendChild(document.createElement(\"div\")),n.id=\"alert-overlay\",n.setAttribute(\"style\",\"position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center\"),n.innerHTML='<div id=\"alert-box\" style=\"background:#fff;padding:1.5em 2em;border-radius:8px;box-shadow:0 2px 16px #0003;min-width:320px;max-width:90vw;max-height:80vh;position:relative;display:flex;flex-direction:column;\">\\n      <button id=\"close-alert\" style=\"position:absolute;top:12px;right:20px;font-size:1.5em;background:none;border:none;cursor:pointer;color:black;\">&times;</button>\\n      <div id=\"alert-list\" style=\"overflow:auto;flex:1;font-family:monospace;font-size:13px;\"></div>\\n    </div>',n.addEventListener(\"click\",e=>e.target==n&&n.remove());const e=document.getElementById(\"close-alert\");e&&(e.onclick=()=>n.remove())}t=document.getElementById(\"alert-list\"),t.innerHTML+=`<div style=\"border-bottom:1px solid #e5e7eb;margin:0.5em 0;padding-bottom:0.5em\">${e}</div>`}function i(e){return e.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\")}function a(){\"undefined\"!=typeof document&&document.addEventListener(\"keydown\",t=>{const n=window.grab;if(n&&n.log&&\"i\"===t.key&&t.ctrlKey&&t.altKey){let t=\"\";for(const o of n.log){const n=new Date(o.lastFetchTime).toLocaleString(),r=e(o.request,0,\"html\"),s=e(o.response,0,\"html\"),a=i(JSON.stringify(o.response,null,2));t+=`\\n          <details open style=\"margin-bottom:0.75em\">\\n            <summary style=\"cursor:pointer;font-weight:bold;padding:4px 0;list-style:none;display:flex;justify-content:space-between\">\\n              <span style=\"color:#1a56db\">${i(o.path)}</span>\\n              <span style=\"color:#6b7280;font-weight:normal;margin-left:1em\">${n}</span>\\n            </summary>\\n            <div style=\"padding-left:1em;margin-top:0.5em\">\\n              <details style=\"margin-bottom:0.4em\">\\n                <summary style=\"cursor:pointer;color:#6b7280;font-size:0.9em\">Request</summary>\\n                <div style=\"padding:0.4em 0 0 1em\">${r}</div>\\n              </details>\\n              <details open style=\"margin-bottom:0.4em\">\\n                <summary style=\"cursor:pointer;color:#6b7280;font-size:0.9em\">Response (structure)</summary>\\n                <div style=\"padding:0.4em 0 0 1em\">${s}</div>\\n              </details>\\n              <details style=\"margin-bottom:0.4em\">\\n                <summary style=\"cursor:pointer;color:#6b7280;font-size:0.9em\">Response (data)</summary>\\n                <pre style=\"margin:0.4em 0 0 1em;padding:0.5em;background:#f9fafb;border:1px solid #e5e7eb;border-radius:4px;overflow:auto;max-height:300px;font-size:12px\">${a}</pre>\\n              </details>\\n            </div>\\n          </details>`}s(t||\"<em>No requests logged yet.</em>\")}})}function l(o){return async function i(a,l){const c=function(e){return{...\"undefined\"!=typeof window?window?.grab?.defaults:globalThis?.grab?.defaults||{},...e}}(l);let{headers:d,response:u,method:f=(c.post?\"POST\":c.put?\"PUT\":c.patch?\"PATCH\":\"GET\"),cache:p,timeout:m=30,baseURL:y=\"undefined\"!=typeof process&&process.env.SERVER_API_URL||\"/api/\",cancelOngoingIfNew:g,cancelNewIfOngoing:b,rateLimit:w,debug:h,infiniteScroll:v,logger:T=t,onRequest:x,onResponse:L,onError:E,onStream:A,unzip:S,dom:O,body:R,post:P,put:k,patch:$,...j}=c;const q=r(y,a);y=q.baseURL,a=q.path;const F=function(e){const t=\"function\"==typeof e?e:null;return{response:!e||t?{}:e,resFunction:t}}(u);let N=F.response,D=F.resFunction;const z=\"undefined\"!=typeof window?window.grab:globalThis.grab,I=z?.log||[];D?N=D({...N,isLoading:!0}):\"object\"==typeof N&&(N.isLoading=!0);try{const r=await async function(e,t,o,r){const{debounce:s=0,repeat:i=0,repeatEvery:a=null,setDefaults:l=!1}=o;if(s>0){const i=await n(async()=>{await r(e,{...t,debounce:0})},1e3*s);return await i(),o.response||{}}if(i>1){for(let n=0;n<i;n++)await r(e,{...t,repeat:0});return o.response||{}}if(a)return setInterval(async()=>{await r(e,{...t,repeat:0,repeatEvery:null})},1e3*a),o.response||{};if(l){const e=\"undefined\"!=typeof window?window.grab:globalThis.grab;return e&&(e.defaults={...t,setDefaults:void 0}),o.response||{}}return null}(a,l||{},c,i);if(r)return r;!function(e,t,n,o){if(\"undefined\"==typeof window)return;const{regrabOnStale:r,regrabOnFocus:s,regrabOnNetwork:i,cache:a,cacheForTime:l=60}=n,c=async()=>await o(e,{...t,cache:!1});r&&a&&setTimeout(c,1e3*l),i&&window.addEventListener(\"online\",c),s&&(window.addEventListener(\"focus\",c),document.addEventListener(\"visibilitychange\",async()=>{\"visible\"===document.visibilityState&&await c()}))}(a,l||{},c,i);let{params:s,priorRequest:u,response:E,paramsAsText:P}=function(e,t,n,o,r,s){const{cache:i,cacheForTime:a,infiniteScroll:l}=n,[c,d]=l||[],u=JSON.stringify(c?{...t,[c]:void 0}:t);let f=s.find(t=>t.request===u&&t.path===e);if(c){let e=(f?.currentPage||0)+1||t?.[c]||1;f?f.currentPage=e:(o[d]=[],e=1),t={...t,[c]:e}}else{for(let e of Object.keys(o))o[e]=void 0;if(i&&f?.response&&(!a||f.lastFetchTime>Date.now()-1e3*a)){for(let e of Object.keys(f.response))o[e]=f.response[e];r&&(o=r(o))}}return{params:t,priorRequest:f,response:o,paramsAsText:u}}(a,j,c,N,D,I);if(j=s,N=E,function(e,n,o,r,s){if(\"undefined\"==typeof window||!o?.length)return;const[i,,a]=o;if(void 0===a)return;let l=\"string\"==typeof a?document.querySelector(a):a;l?(window.scrollListener&&\"function\"==typeof l.removeEventListener&&l.removeEventListener(\"scroll\",window.scrollListener),window.scrollListener=t=>{const o=t.target;localStorage.setItem(\"scroll\",JSON.stringify([o.scrollTop,o.scrollLeft,a])),o.scrollHeight-o.scrollTop<=o.clientHeight+200&&s(e,{...n,cache:!1,[i]:(r?.currentPage||0)+1})},l.addEventListener(\"scroll\",window.scrollListener)):t(\"paginateDOM not found\",{color:\"red\"})}(a,l,v,u,i),w>0&&u?.lastFetchTime>Date.now()-1e3*w)throw new Error(`Fetch rate limit exceeded for ${a}. Wait ${w}s between requests.`);if(u?.controller)if(g)u.controller.abort();else if(b)return{isLoading:!0};const k=new AbortController;let $=k.signal;g||\"function\"!=typeof AbortSignal.timeout||($=AbortSignal.timeout(1e3*m)),I.unshift({path:a,request:P,lastFetchTime:Date.now(),controller:k});let{fetchParams:q,paramsGETRequest:F}=function(e,t,n,o,r,s){const i=[\"POST\",\"PUT\",\"PATCH\"].includes(e),a={method:e,headers:{\"Content-Type\":\"application/json\",Accept:\"application/json\",...t},body:n||(i?JSON.stringify(o):null),redirect:\"follow\",cache:r?\"force-cache\":\"no-store\",signal:s};let l=\"\";return i||(l=(Object.keys(o).length?\"?\":\"\")+new URLSearchParams(o).toString()),{fetchParams:a,paramsGETRequest:l}}(f,d,R,j,!!p,$);if(\"function\"==typeof x){const e=x(a,N,j,q);Array.isArray(e)&&([a,N,j,q]=e)}const z=/* @__PURE__ */new Date,U=await o(y,a,F,q,j,A,S,O);if(D?N=D({...N,isLoading:void 0}):\"object\"==typeof N&&delete N.isLoading,\"function\"==typeof L){const e=L(a,N,j,q);Array.isArray(e)&&([a,N,j,q]=e)}const H=((Number(/* @__PURE__ */new Date)-Number(z))/1e3).toFixed(1);h&&T(`Path:${y+a+F}\\n${JSON.stringify(l,null,2)}\\nTime: ${H}s\\nResponse: ${e(U)}`);const[,W]=v||[];return N=function(e,t,n,o){if(\"object\"==typeof e&&null!==e){for(let n of Object.keys(e))t[n]=o===n&&Array.isArray(t[n])?[...t[n],...e[n]]:e[n];t.data=e}else n?t=n({data:e,...e}):\"object\"==typeof t&&(t.data=e);return t}(U,N,D,W),I[0]&&(I[0].response=N),D&&(N=D(N)),N}catch(U){if(\"function\"==typeof E&&E(U.message,y+a,j),c.retryAttempts&&c.retryAttempts>0)return await i(a,{...l,retryAttempts:--c.retryAttempts});!U.message.includes(\"signal\")&&h&&(T(`Error: ${U.message}\\nPath:${y+a}\\n`,{color:\"red\"}),\"undefined\"!=typeof document&&s(U.message)),N=N||{},N.error=U.message;const e=\"function\"==typeof u?u:null;return e?(N.data=e({isLoading:void 0,error:U.message}),N=N.data):delete N.isLoading,N}}}export{s as a,r as b,l as c,n as d,a as s,o as w};\n//# sourceMappingURL=core-BX1uvK6L.js.map\n","import{w as t,s as r,c as o}from\"./core-BX1uvK6L.js\";import{b as e,d as n,a}from\"./core-BX1uvK6L.js\";import{log as s}from\"./log.es.js\";async function i(t){const{extract:r}=await import(\"./archiver-web.es.js\"),o=await r({archiveBuffer:t}),e={};for(const n of o)e[n.path]=n.content;return e}async function c(t,r){const{parseHTML:o}=await import(\"./index-CLlAnStL.js\"),{document:e}=o(t);if(!r||!0===r)return e;const n=e.querySelector(r);return n?n.innerHTML??n.textContent:null}const l=o(async function(r,o,e,n,a,s,l,d){const p=\"undefined\"!=typeof window?window.grab:globalThis.grab,u=p?.mock?.[o],f=JSON.stringify(a);if(u&&(!u.method||u.method===n.method)&&(!u.params||f===JSON.stringify(u.params)))return await t(u.delay||0),\"function\"==typeof u.response?u.response(a):u.response;const w=await fetch(r+o+e,n).catch(t=>{throw new Error(t.message)});if(!w.ok)throw new Error(`HTTP error: ${w.status} ${w.statusText}`);if(s)return await s(w.body),null;const h=w.headers.get(\"content-type\")??\"\",g=!1!==l&&(h.includes(\"application/zip\")||h.includes(\"application/x-zip\")),m=!1!==d&&(\"string\"==typeof d||h.includes(\"text/html\"));if(g){const t=await w.arrayBuffer().catch(t=>{throw new Error(\"Error reading zip: \"+t)});return{data:await i(t)}}if(m){const t=await w.text().catch(t=>{throw new Error(\"Error reading html: \"+t)});return{data:await c(t,\"string\"!=typeof d||d)}}return await(h.includes(\"application/json\")?w.json():h.includes(\"application/pdf\")||h.includes(\"application/octet-stream\")?w.blob():h?w.text():w.json()).catch(t=>{throw new Error(\"Error parsing response: \"+t)})}),d=l;d.instance=(t={})=>(r,o={})=>l(r,{...t,...o}),d.log=[],d.mock={},d.defaults={},\"undefined\"!=typeof window?(window.log=s,window.grab=d,r(),document.addEventListener(\"DOMContentLoaded\",()=>{try{const t=localStorage.getItem(\"scroll\");if(!t)return;const[r,o,e]=JSON.parse(t);if(!r||!e)return;const n=document.querySelector(e);n&&(n.scrollTop=r,n.scrollLeft=o)}catch(t){console.warn(\"Failed to restore scroll position\",t)}})):\"undefined\"!=typeof globalThis&&(globalThis.log=s,globalThis.grab=d);export{e as buildUrl,n as debouncer,d as default,d as grab,s as log,r as setupDevTools,a as showAlert,t as wait};\n//# sourceMappingURL=grab-api.es.js.map\n","/**\r\n * @fileoverview HTTP client implementation using grab-url\r\n */\r\n\r\nimport grab from 'grab-url';\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\nimport { HttpClient, RequestsProxyConfigDict } from '../types';\r\n\r\n/**\r\n * Simple HTTP client implementation using grab-url with proxy support.\r\n * This client handles HTTP/HTTPS requests with configurable headers and proxy settings.\r\n *\r\n * @internal\r\n */\r\nexport class FetchHttpClient implements HttpClient {\r\n  private requestHeaders: Record<string, string> = {};\r\n  private proxyConfiguration: Partial<RequestsProxyConfigDict> = {};\r\n\r\n  /**\r\n   * Creates a new FetchHttpClient instance.\r\n   * Sets default Accept-Language header to English.\r\n   */\r\n  constructor() {\r\n    this.requestHeaders['Accept-Language'] = 'en-US';\r\n  }\r\n\r\n  /**\r\n   * Sets a custom header for all requests.\r\n   *\r\n   * @param {string} headerName - The header name\r\n   * @param {string} headerValue - The header value\r\n   */\r\n  setHeader(headerName: string, headerValue: string): void {\r\n    this.requestHeaders[headerName] = headerValue;\r\n  }\r\n\r\n  /**\r\n   * Configures proxy settings for all requests.\r\n   *\r\n   * @param {RequestsProxyConfigDict} proxies - Proxy configuration dictionary\r\n   */\r\n  setProxies(proxies: RequestsProxyConfigDict): void {\r\n    this.proxyConfiguration = proxies;\r\n  }\r\n\r\n  /**\r\n   * Performs an HTTP GET request.\r\n   *\r\n   * @param {string} url - The URL to request\r\n   * @param {RequestInit} [options] - Additional request options\r\n   * @returns {Promise<Response>} The HTTP response\r\n   */\r\n  async get(url: string, options?: RequestInit): Promise<Response> {\r\n    const proxyAgent = this.getProxyAgentForUrl(url);\r\n    return grab(url, {\r\n      ...options,\r\n      method: 'GET',\r\n      headers: { ...this.requestHeaders, ...(options?.headers as Record<string, string>) },\r\n      agent: proxyAgent\r\n    } as any);\r\n  }\r\n\r\n  /**\r\n   * Performs an HTTP POST request.\r\n   *\r\n   * @param {string} url - The URL to request\r\n   * @param {RequestInit} [options] - Additional request options\r\n   * @returns {Promise<Response>} The HTTP response\r\n   */\r\n  async post(url: string, options?: RequestInit): Promise<Response> {\r\n    const proxyAgent = this.getProxyAgentForUrl(url);\r\n    return grab(url, {\r\n      ...options,\r\n      method: 'POST',\r\n      headers: { ...this.requestHeaders, ...(options?.headers as Record<string, string>) },\r\n      agent: proxyAgent\r\n    } as any);\r\n  }\r\n\r\n  /**\r\n   * Gets the appropriate proxy agent for a given URL.\r\n   *\r\n   * @private\r\n   * @param {string} url - The URL being requested\r\n   * @returns {any} The proxy agent or undefined if no proxy is configured\r\n   */\r\n  private getProxyAgentForUrl(url: string): any {\r\n    if (Object.keys(this.proxyConfiguration).length === 0) {\r\n      return undefined;\r\n    }\r\n\r\n    const proxyUrl = url.startsWith('https://')\r\n      ? this.proxyConfiguration.https\r\n      : this.proxyConfiguration.http;\r\n\r\n    if (!proxyUrl) {\r\n      return undefined;\r\n    }\r\n\r\n    return new HttpsProxyAgent(proxyUrl);\r\n  }\r\n}\r\n","/**\r\n * @fileoverview Main API class for the YouTube Transcript API\r\n */\r\n\r\nimport { ProxyConfig } from './proxies';\r\nimport { FetchedTranscript, TranscriptList } from './models';\r\nimport { TranscriptListFetcher } from './fetchers/transcript-list-fetcher';\r\nimport { HttpClient } from './types';\r\nimport { FetchHttpClient } from './http/fetch-http-client';\r\n\r\n/**\r\n * Main API class for retrieving YouTube transcripts.\r\n * This is the primary entry point for the library.\r\n *\r\n * @example\r\n * ```typescript\r\n * // Basic usage\r\n * const api = new YouTubeTranscriptApi();\r\n * const transcript = await api.fetchTranscript('video_id');\r\n *\r\n * // With language preference\r\n * const transcript = await api.fetchTranscript('video_id', { languages: ['de', 'en'] });\r\n *\r\n * // With proxy\r\n * import { GenericProxyConfig } from 'youtube-transcript-api';\r\n * const api = new YouTubeTranscriptApi({\r\n *   proxyConfig: new GenericProxyConfig({\r\n *     httpUrl: 'http://user:pass@proxy.example.com:8080'\r\n *   })\r\n * });\r\n * ```\r\n */\r\nexport class YouTubeTranscriptApi {\r\n  private transcriptListFetcher: TranscriptListFetcher;\r\n\r\n  /**\r\n   * Creates a new YouTubeTranscriptApi instance.\r\n   *\r\n   * Note on thread-safety: As this class initializes an HTTP client,\r\n   * it is not thread-safe. Make sure to initialize an instance per\r\n   * thread if used in a multi-threading scenario!\r\n   *\r\n   * @param {Object} [options] - Configuration options\r\n   * @param {ProxyConfig} [options.proxyConfig] - An optional ProxyConfig object defining proxies\r\n   *   used for all network requests. This can be used to work around your IP being blocked\r\n   *   by YouTube, as described in the \"Working around IP bans\" section of the README.\r\n   * @param {HttpClient} [options.httpClient] - You can optionally pass in a custom HTTP client\r\n   *   if you want to share state between different instances of YouTubeTranscriptApi or\r\n   *   customize request behavior.\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * // Basic usage\r\n   * const api = new YouTubeTranscriptApi();\r\n   *\r\n   * // With Webshare proxy\r\n   * import { WebshareProxyConfig } from 'youtube-transcript-api';\r\n   * const api = new YouTubeTranscriptApi({\r\n   *   proxyConfig: new WebshareProxyConfig({\r\n   *     proxyUsername: 'your-username',\r\n   *     proxyPassword: 'your-password',\r\n   *     filterIpLocations: ['us', 'de']\r\n   *   })\r\n   * });\r\n   *\r\n   * // With generic proxy\r\n   * import { GenericProxyConfig } from 'youtube-transcript-api';\r\n   * const api = new YouTubeTranscriptApi({\r\n   *   proxyConfig: new GenericProxyConfig({\r\n   *     httpUrl: 'http://user:pass@proxy.example.com:8080',\r\n   *     httpsUrl: 'https://user:pass@proxy.example.com:8080'\r\n   *   })\r\n   * });\r\n   * ```\r\n   */\r\n  constructor(options?: { proxyConfig?: ProxyConfig; httpClient?: HttpClient }) {\r\n    const httpClient = this.initializeHttpClient(options);\r\n    this.configureHttpClientWithProxy(httpClient, options?.proxyConfig);\r\n    this.transcriptListFetcher = new TranscriptListFetcher(httpClient, options?.proxyConfig || null);\r\n  }\r\n\r\n  /**\r\n   * Initializes the HTTP client from options or creates a default one.\r\n   *\r\n   * @private\r\n   * @param {Object} [options] - Configuration options\r\n   * @returns {HttpClient} The initialized HTTP client\r\n   */\r\n  private initializeHttpClient(options?: { httpClient?: HttpClient }): HttpClient {\r\n    return options?.httpClient || new FetchHttpClient();\r\n  }\r\n\r\n  /**\r\n   * Configures the HTTP client with proxy settings if provided.\r\n   *\r\n   * @private\r\n   * @param {HttpClient} httpClient - The HTTP client to configure\r\n   * @param {ProxyConfig} [proxyConfig] - Optional proxy configuration\r\n   */\r\n  private configureHttpClientWithProxy(httpClient: HttpClient, proxyConfig?: ProxyConfig): void {\r\n    if (!proxyConfig || !(httpClient instanceof FetchHttpClient)) {\r\n      return;\r\n    }\r\n\r\n    const proxyDictionary = proxyConfig.toRequestsDict();\r\n    httpClient.setProxies(proxyDictionary);\r\n\r\n    if (proxyConfig.preventKeepingConnectionsAlive) {\r\n      httpClient.setHeader('Connection', 'close');\r\n    }\r\n\r\n    // Note: Retry logic for 429 status codes with configured retries\r\n    // would need to be implemented in the HTTP client or fetcher\r\n  }\r\n\r\n  /**\r\n   * Retrieves the transcript for a single video.\r\n   * This is a shortcut for calling:\r\n   * `api.listTranscripts(videoId).then(list => list.findTranscript(languages)).then(t => t.fetch())`\r\n   *\r\n   * @param {string} videoId - The ID of the video you want to retrieve the transcript for.\r\n   *   Make sure that this is the actual ID, NOT the full URL to the video!\r\n   * @param {Object} [options] - Fetch options\r\n   * @param {string[]} [options.languages=['en']] - A list of language codes in descending priority.\r\n   *   For example, if this is set to ['de', 'en'] it will first try to fetch the german\r\n   *   transcript (de) and then fetch the english transcript (en) if it fails to do so.\r\n   * @param {boolean} [options.preserveFormatting=false] - Whether to keep select HTML text formatting\r\n   * @returns {Promise<FetchedTranscript>} The fetched transcript\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const api = new YouTubeTranscriptApi();\r\n   *\r\n   * // Fetch English transcript\r\n   * const transcript = await api.fetchTranscript('dQw4w9WgXcQ');\r\n   *\r\n   * // Fetch with language preference\r\n   * const transcript = await api.fetchTranscript('dQw4w9WgXcQ', {\r\n   *   languages: ['de', 'en']\r\n   * });\r\n   *\r\n   * // Preserve HTML formatting\r\n   * const transcript = await api.fetchTranscript('dQw4w9WgXcQ', {\r\n   *   preserveFormatting: true\r\n   * });\r\n   *\r\n   * // Iterate over snippets\r\n   * for (const snippet of transcript) {\r\n   *   console.log(`${snippet.start}s: ${snippet.text}`);\r\n   * }\r\n   * ```\r\n   */\r\n  async fetchTranscript(\r\n    videoId: string,\r\n    options?: {\r\n      languages?: string[];\r\n      preserveFormatting?: boolean;\r\n    }\r\n  ): Promise<FetchedTranscript> {\r\n    const preferredLanguages = options?.languages || ['en'];\r\n    const preserveFormatting = options?.preserveFormatting || false;\r\n\r\n    const transcriptList = await this.listTranscripts(videoId);\r\n    const transcript = transcriptList.findTranscript(preferredLanguages);\r\n    return transcript.fetch(preserveFormatting);\r\n  }\r\n\r\n  /**\r\n   * Retrieves the list of transcripts which are available for a given video.\r\n   * It returns a TranscriptList object which is iterable and provides methods to\r\n   * filter the list of transcripts for specific languages.\r\n   *\r\n   * While iterating over the TranscriptList, the individual transcripts are\r\n   * represented by Transcript objects, which provide metadata and can either\r\n   * be fetched by calling transcript.fetch() or translated by calling\r\n   * transcript.translate('en').\r\n   *\r\n   * @param {string} videoId - The ID of the video you want to retrieve the transcript for.\r\n   *   Make sure that this is the actual ID, NOT the full URL to the video!\r\n   * @returns {Promise<TranscriptList>} A list of available transcripts\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const api = new YouTubeTranscriptApi();\r\n   *\r\n   * // Retrieve available transcripts\r\n   * const transcriptList = await api.listTranscripts('video_id');\r\n   *\r\n   * // Iterate over all available transcripts\r\n   * for (const transcript of transcriptList) {\r\n   *   console.log(\r\n   *     transcript.videoId,\r\n   *     transcript.language,\r\n   *     transcript.languageCode,\r\n   *     transcript.isGenerated,\r\n   *     transcript.isTranslatable,\r\n   *     transcript.translationLanguages\r\n   *   );\r\n   *\r\n   *   // Fetch the actual transcript data\r\n   *   const fetched = await transcript.fetch();\r\n   *   console.log(fetched);\r\n   *\r\n   *   // Translate the transcript\r\n   *   if (transcript.isTranslatable) {\r\n   *     const translated = transcript.translate('de');\r\n   *     const fetchedTranslated = await translated.fetch();\r\n   *     console.log(fetchedTranslated);\r\n   *   }\r\n   * }\r\n   *\r\n   * // Filter for specific language\r\n   * const transcript = transcriptList.findTranscript(['de', 'en']);\r\n   *\r\n   * // Filter for manually created transcripts\r\n   * const manual = transcriptList.findManuallyCreatedTranscript(['de', 'en']);\r\n   *\r\n   * // Filter for automatically generated transcripts\r\n   * const generated = transcriptList.findGeneratedTranscript(['de', 'en']);\r\n   * ```\r\n   */\r\n  async listTranscripts(videoId: string): Promise<TranscriptList> {\r\n    return this.transcriptListFetcher.fetchTranscriptList(videoId);\r\n  }\r\n\r\n  /**\r\n   * Alias for fetchTranscript() for backward compatibility.\r\n   *\r\n   * @deprecated Use fetchTranscript() instead\r\n   * @param {string} videoId - The video ID\r\n   * @param {Object} [options] - Fetch options\r\n   * @returns {Promise<FetchedTranscript>} The fetched transcript\r\n   */\r\n  async fetch(\r\n    videoId: string,\r\n    options?: {\r\n      languages?: string[];\r\n      preserveFormatting?: boolean;\r\n    }\r\n  ): Promise<FetchedTranscript> {\r\n    return this.fetchTranscript(videoId, options);\r\n  }\r\n\r\n  /**\r\n   * Alias for listTranscripts() for backward compatibility.\r\n   *\r\n   * @deprecated Use listTranscripts() instead\r\n   * @param {string} videoId - The video ID\r\n   * @returns {Promise<TranscriptList>} A list of available transcripts\r\n   */\r\n  async list(videoId: string): Promise<TranscriptList> {\r\n    return this.listTranscripts(videoId);\r\n  }\r\n}\r\n","/**\r\n * @fileoverview Transcript timing utilities: encode/decode run-length compressed\r\n * playback speeds and interpolate timestamps from character offsets.\r\n */\r\n\r\nimport { FetchedTranscript } from '../models';\r\n\r\n/**\r\n * Encodes speech speed from a fetched transcript into a compact run-length string.\r\n * Optionally prepends a YouTube embed iframe with data-timestamps for player sync.\r\n * @param transcript - A FetchedTranscript returned by fetchTranscript()\r\n * @param addPlayer - If true, prepends an iframe embed with data-timestamps attribute\r\n * @returns html content, word count, run-length encoded speed string, and raw timestamps\r\n */\r\nexport function encodeTranscriptSpeeds(\r\n  transcript: FetchedTranscript,\r\n  addPlayer = false\r\n): {\r\n  html: string;\r\n  word_count: number;\r\n  speeds: string;\r\n} {\r\n  let charCount = 0;\r\n  const timestamps: [number, number][] = [];\r\n  const textParts: string[] = [];\r\n\r\n  for (const snippet of transcript.snippets) {\r\n    textParts.push(snippet.text);\r\n    charCount += snippet.text.length;\r\n    if (snippet.start > 0) {\r\n      timestamps.push([charCount, snippet.start]);\r\n    }\r\n  }\r\n\r\n  const content = textParts.join(' ').replace(/\\s+/g, ' ');\r\n  const word_count = content.split(/\\s+/).filter(Boolean).length;\r\n\r\n  const speedValues = timestamps.map(([char, time]) => Math.floor(char / time) - 10);\r\n  if (speedValues.length === 0) {\r\n    return { html: content, word_count, speeds: '' };\r\n  }\r\n\r\n  const compressed: number[] = [];\r\n  const compressedCount: number[] = [];\r\n  let currentNum = speedValues[0];\r\n  let count = 1;\r\n\r\n  for (let i = 1; i < speedValues.length; i++) {\r\n    if (speedValues[i] === currentNum) {\r\n      count++;\r\n    } else {\r\n      compressed.push(currentNum);\r\n      compressedCount.push(count);\r\n      currentNum = speedValues[i];\r\n      count = 1;\r\n    }\r\n  }\r\n  compressed.push(currentNum);\r\n  compressedCount.push(count);\r\n\r\n  const speeds = compressed.map((val, i) => `${val}x${compressedCount[i]}`).join(',');\r\n\r\n  const html = addPlayer\r\n    ? `<iframe width=\"100%\" height=\"315px\" data-timestamps=\"${speeds}\" ` +\r\n      `src=\"https://www.youtube.com/embed/${transcript.videoId}\" frameborder=\"0\" ` +\r\n      `allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; ` +\r\n      `gyroscope; picture-in-picture\" allowfullscreen></iframe>${content}`\r\n    : content;\r\n\r\n  return { html, word_count, speeds };\r\n}\r\n\r\n/**\r\n * Given a character index in the joined transcript text, returns the interpolated\r\n * video timestamp in seconds. Uses the nearest snippet boundaries to interpolate.\r\n */\r\nexport function getTimestampAtChar(transcript: FetchedTranscript, charIndex: number): number {\r\n  let charCount = 0;\r\n  const points: [number, number][] = [[0, 0]]; // [cumulativeChars, seconds]\r\n\r\n  for (const snippet of transcript.snippets) {\r\n    charCount += snippet.text.length + 1; // +1 for the join space\r\n    points.push([charCount, snippet.start]);\r\n  }\r\n\r\n  if (charIndex <= 0) return 0;\r\n  if (charIndex >= points[points.length - 1][0]) return points[points.length - 1][1];\r\n\r\n  for (let i = 1; i < points.length; i++) {\r\n    if (points[i][0] >= charIndex) {\r\n      const [c0, t0] = points[i - 1];\r\n      const [c1, t1] = points[i];\r\n      const ratio = (charIndex - c0) / (c1 - c0);\r\n      return t0 + ratio * (t1 - t0);\r\n    }\r\n  }\r\n\r\n  return points[points.length - 1][1];\r\n}\r\n\r\n/**\r\n * Decompresses a speeds string produced by encodeTranscriptSpeeds back into a flat array.\r\n * Format: \"12x3,8x7,15x1\" → [12,12,12, 8,8,8,8,8,8,8, 15]\r\n */\r\nexport function decompressTimestampsArray(speeds: string): number[] {\r\n  const decompressed: number[] = [];\r\n  for (const part of speeds.split(',')) {\r\n    const [num, count] = part.split('x');\r\n    decompressed.push(...Array(parseInt(count)).fill(parseInt(num)));\r\n  }\r\n  return decompressed;\r\n}\r\n","/**\r\n * @fileoverview Proxy configuration classes for the YouTube Transcript API\r\n */\r\n\r\nimport { RequestsProxyConfigDict } from '../types';\r\n\r\n/**\r\n * Exception for invalid proxy configurations\r\n */\r\nexport class InvalidProxyConfig extends Error {\r\n  constructor(message: string) {\r\n    super(message);\r\n    this.name = 'InvalidProxyConfig';\r\n    Object.setPrototypeOf(this, InvalidProxyConfig.prototype);\r\n  }\r\n}\r\n\r\n/**\r\n * Base class for all proxy configurations.\r\n * Any proxy config can be used as long as it can be converted to a RequestsProxyConfigDict.\r\n */\r\nexport abstract class ProxyConfig {\r\n  /**\r\n   * Converts this proxy config to a dictionary that can be used by HTTP clients.\r\n   * @returns {RequestsProxyConfigDict} The proxy configuration dictionary\r\n   */\r\n  abstract toRequestsDict(): RequestsProxyConfigDict;\r\n\r\n  /**\r\n   * If you are using rotating proxies, it can be useful to prevent the HTTP\r\n   * client from keeping TCP connections alive, as your IP won't be rotated on\r\n   * every request if your connection stays open.\r\n   * @returns {boolean} Whether to prevent keeping connections alive\r\n   */\r\n  get preventKeepingConnectionsAlive(): boolean {\r\n    return false;\r\n  }\r\n\r\n  /**\r\n   * Defines how many times we should retry if a request is blocked. When using\r\n   * rotating residential proxies with a large IP pool it can make sense to retry a\r\n   * couple of times when a blocked IP is encountered, since a retry will trigger\r\n   * an IP rotation and the next IP might not be blocked.\r\n   * @returns {number} Number of retries when blocked\r\n   */\r\n  get retriesWhenBlocked(): number {\r\n    return 0;\r\n  }\r\n}\r\n\r\n/**\r\n * Generic proxy configuration that can be used to set up any HTTP/HTTPS/SOCKS proxy.\r\n *\r\n * If only an HTTP or an HTTPS proxy is provided, it will be used for both types of\r\n * connections. However, you will have to provide at least one of the two.\r\n *\r\n * @example\r\n * ```typescript\r\n * const proxyConfig = new GenericProxyConfig({\r\n *   httpUrl: 'http://user:pass@proxy.example.com:8080',\r\n *   httpsUrl: 'https://user:pass@proxy.example.com:8080'\r\n * });\r\n * ```\r\n */\r\nexport class GenericProxyConfig extends ProxyConfig {\r\n  protected _httpUrl: string | null;\r\n  protected _httpsUrl: string | null;\r\n\r\n  /**\r\n   * Creates a generic proxy configuration.\r\n   * If only an HTTP or an HTTPS proxy is provided, it will be used for both types of\r\n   * connections. However, you will have to provide at least one of the two.\r\n   *\r\n   * @param {Object} options - Proxy configuration options\r\n   * @param {string} [options.httpUrl] - The proxy URL used for HTTP requests. Defaults to httpsUrl if not provided.\r\n   * @param {string} [options.httpsUrl] - The proxy URL used for HTTPS requests. Defaults to httpUrl if not provided.\r\n   * @throws {InvalidProxyConfig} If neither httpUrl nor httpsUrl is provided\r\n   */\r\n  constructor(options: { httpUrl?: string; httpsUrl?: string }) {\r\n    super();\r\n    const { httpUrl, httpsUrl } = options;\r\n\r\n    if (!httpUrl && !httpsUrl) {\r\n      throw new InvalidProxyConfig(\r\n        'GenericProxyConfig requires you to define at least one of the two: http or https'\r\n      );\r\n    }\r\n\r\n    this._httpUrl = httpUrl || null;\r\n    this._httpsUrl = httpsUrl || null;\r\n  }\r\n\r\n  get httpUrl(): string | null {\r\n    return this._httpUrl;\r\n  }\r\n\r\n  get httpsUrl(): string | null {\r\n    return this._httpsUrl;\r\n  }\r\n\r\n  toRequestsDict(): RequestsProxyConfigDict {\r\n    return {\r\n      http: this.httpUrl || this.httpsUrl!,\r\n      https: this.httpsUrl || this.httpUrl!\r\n    };\r\n  }\r\n}\r\n\r\n/**\r\n * Webshare proxy configuration for rotating residential proxies.\r\n *\r\n * Webshare is a provider offering rotating residential proxies, which is the\r\n * most reliable way to work around being blocked by YouTube.\r\n *\r\n * If you don't have a Webshare account yet, you will have to create one\r\n * at https://www.webshare.io/?referral_code=w0xno53eb50g and purchase a \"Residential\"\r\n * proxy package that suits your workload (make sure NOT to purchase \"Proxy Server\"\r\n * or \"Static Residential\"!).\r\n *\r\n * Once you have created an account you only need the \"Proxy Username\" and\r\n * \"Proxy Password\" that you can find in your Webshare settings to set up this config.\r\n *\r\n * @example\r\n * ```typescript\r\n * const proxyConfig = new WebshareProxyConfig({\r\n *   proxyUsername: 'your-username',\r\n *   proxyPassword: 'your-password',\r\n *   filterIpLocations: ['us', 'de']\r\n * });\r\n * ```\r\n */\r\nexport class WebshareProxyConfig extends GenericProxyConfig {\r\n  public static readonly DEFAULT_DOMAIN_NAME = 'p.webshare.io';\r\n  public static readonly DEFAULT_PORT = 80;\r\n\r\n  public readonly proxyUsername: string;\r\n  public readonly proxyPassword: string;\r\n  public readonly domainName: string;\r\n  public readonly proxyPort: number;\r\n  private readonly filterIpLocations: string[];\r\n  private readonly _retriesWhenBlocked: number;\r\n\r\n  /**\r\n   * Creates a Webshare proxy configuration.\r\n   *\r\n   * Once you have created a Webshare account and purchased a \"Residential\" package\r\n   * (make sure NOT to purchase \"Proxy Server\" or \"Static Residential\"!), this config\r\n   * class allows you to easily use it by defaulting to the most reliable proxy settings\r\n   * (rotating residential proxies).\r\n   *\r\n   * @param {Object} options - Webshare configuration options\r\n   * @param {string} options.proxyUsername - \"Proxy Username\" found at https://dashboard.webshare.io/proxy/settings\r\n   * @param {string} options.proxyPassword - \"Proxy Password\" found at https://dashboard.webshare.io/proxy/settings\r\n   * @param {string[]} [options.filterIpLocations] - If you want to limit the pool of IPs to specific countries,\r\n   *   provide a list of location codes (e.g., ['us', 'de']). This can reduce latency and work around location-based restrictions.\r\n   * @param {number} [options.retriesWhenBlocked=10] - How many times to retry if a request is blocked\r\n   * @param {string} [options.domainName] - Custom domain name (defaults to p.webshare.io)\r\n   * @param {number} [options.proxyPort] - Custom proxy port (defaults to 80)\r\n   */\r\n  constructor(options: {\r\n    proxyUsername: string;\r\n    proxyPassword: string;\r\n    filterIpLocations?: string[];\r\n    retriesWhenBlocked?: number;\r\n    domainName?: string;\r\n    proxyPort?: number;\r\n  }) {\r\n    super({ httpUrl: 'placeholder' });\r\n\r\n    this.proxyUsername = options.proxyUsername;\r\n    this.proxyPassword = options.proxyPassword;\r\n    this.domainName = options.domainName || WebshareProxyConfig.DEFAULT_DOMAIN_NAME;\r\n    this.proxyPort = options.proxyPort || WebshareProxyConfig.DEFAULT_PORT;\r\n    this.filterIpLocations = options.filterIpLocations || [];\r\n    this._retriesWhenBlocked = options.retriesWhenBlocked ?? 10;\r\n  }\r\n\r\n  /**\r\n   * Gets the full proxy URL with location filtering and rotation settings.\r\n   * @returns {string} The complete proxy URL\r\n   */\r\n  get url(): string {\r\n    const locationCodes = this.filterIpLocations\r\n      .map(code => `-${code.toUpperCase()}`)\r\n      .join('');\r\n\r\n    let username = this.proxyUsername;\r\n    const suffix = '-rotate';\r\n\r\n    if (username.endsWith(suffix)) {\r\n      username = username.slice(0, -suffix.length);\r\n    }\r\n\r\n    return (\r\n      `http://${username}${locationCodes}${suffix}:${this.proxyPassword}` +\r\n      `@${this.domainName}:${this.proxyPort}/`\r\n    );\r\n  }\r\n\r\n  get httpUrl(): string {\r\n    return this.url;\r\n  }\r\n\r\n  get httpsUrl(): string {\r\n    return this.url;\r\n  }\r\n\r\n  toRequestsDict(): RequestsProxyConfigDict {\r\n    return {\r\n      http: this.url,\r\n      https: this.url\r\n    };\r\n  }\r\n\r\n  get preventKeepingConnectionsAlive(): boolean {\r\n    return true;\r\n  }\r\n\r\n  get retriesWhenBlocked(): number {\r\n    return this._retriesWhenBlocked;\r\n  }\r\n}\r\n","/**\r\n * @fileoverview All formatter classes for transcript formatting\r\n */\r\n\r\nimport type { FetchedTranscript } from '../models/fetched-transcript';\r\nimport type { FetchedTranscriptSnippet } from '../types';\r\n\r\n/**\r\n * Base abstract class for all transcript formatters.\r\n */\r\nexport abstract class Formatter {\r\n  abstract formatTranscript(transcript: FetchedTranscript, options?: any): string;\r\n  abstract formatTranscripts(transcripts: FetchedTranscript[], options?: any): string;\r\n}\r\n\r\n/**\r\n * Formatter that converts transcripts to pretty-printed JSON format.\r\n */\r\nexport class PrettyPrintFormatter extends Formatter {\r\n  formatTranscript(transcript: FetchedTranscript): string {\r\n    return JSON.stringify(transcript.toRawData(), null, 2);\r\n  }\r\n\r\n  formatTranscripts(transcripts: FetchedTranscript[]): string {\r\n    return JSON.stringify(transcripts.map(t => t.toRawData()), null, 2);\r\n  }\r\n}\r\n\r\n/**\r\n * Formatter that converts transcripts to JSON format.\r\n */\r\nexport class JSONFormatter extends Formatter {\r\n  formatTranscript(transcript: FetchedTranscript, options?: { indent?: number }): string {\r\n    return JSON.stringify(transcript.toRawData(), null, options?.indent);\r\n  }\r\n\r\n  formatTranscripts(transcripts: FetchedTranscript[], options?: { indent?: number }): string {\r\n    return JSON.stringify(transcripts.map(t => t.toRawData()), null, options?.indent);\r\n  }\r\n}\r\n\r\n/**\r\n * Formatter that converts transcripts to plain text format with no timestamps.\r\n */\r\nexport class TextFormatter extends Formatter {\r\n  formatTranscript(transcript: FetchedTranscript): string {\r\n    return transcript.snippets.map(snippet => snippet.text).join('\\n');\r\n  }\r\n\r\n  formatTranscripts(transcripts: FetchedTranscript[]): string {\r\n    return transcripts.map(t => this.formatTranscript(t)).join('\\n\\n\\n');\r\n  }\r\n}\r\n\r\n/**\r\n * Formatter that converts transcripts to article format with character-to-timestamp mappings.\r\n * Returns full text as a string along with character position to time pairings.\r\n */\r\nexport class ArticleFormatter extends Formatter {\r\n  formatTranscript(transcript: FetchedTranscript): string {\r\n    // Build full text content\r\n    const fullText = transcript.snippets.map(snippet => snippet.text).join(' ');\r\n\r\n    // Build timestamp mappings - track which character position corresponds to which time\r\n    const timestamps: Array<[number, number]> = [];\r\n    let charPosition = 0;\r\n\r\n    for (const snippet of transcript.snippets) {\r\n      // Record the character position and start time for this snippet\r\n      timestamps.push([charPosition, snippet.start]);\r\n      charPosition += snippet.text.length + 1; // +1 for the space separator\r\n    }\r\n\r\n    // Calculate character-per-second speeds at intervals\r\n    const speedsEveryCharPeriod: { [key: number]: number } = {};\r\n    const valueCharPeriod = 100;\r\n\r\n    for (const [char, time] of timestamps) {\r\n      const speed = Math.floor(char / time) - 10;\r\n      speedsEveryCharPeriod[Math.floor(char / valueCharPeriod)] = speed;\r\n    }\r\n\r\n    // Compress the speed data\r\n    const speeds = Object.keys(speedsEveryCharPeriod).map(\r\n      (timeKey) => speedsEveryCharPeriod[parseInt(timeKey)]\r\n    );\r\n\r\n    const compressed: number[] = [];\r\n    const compressedCount: number[] = [];\r\n    let currentNum = speeds[0];\r\n    let count = 1;\r\n\r\n    for (let i = 1; i < speeds.length; i++) {\r\n      if (speeds[i] === currentNum) {\r\n        count++;\r\n      } else {\r\n        compressed.push(currentNum);\r\n        compressedCount.push(count);\r\n        currentNum = speeds[i];\r\n        count = 1;\r\n      }\r\n    }\r\n    compressed.push(currentNum);\r\n    compressedCount.push(count);\r\n\r\n    // Convert counts to cumulative positions\r\n    let total = 0;\r\n    const cumulativeCounts = compressedCount.map((c) => {\r\n      total += c;\r\n      return total;\r\n    });\r\n\r\n    // Format output with speeds and timestamps\r\n    const speedsData = compressed.join(',') + '  ' + cumulativeCounts.join(',');\r\n\r\n    // Remove extra spaces from full text\r\n    const cleanedText = fullText.replace(/\\s+/g, ' ');\r\n\r\n    return JSON.stringify({\r\n      text: cleanedText,\r\n      timestamps: speedsData,\r\n      wordCount: cleanedText.split(/\\s+/).length,\r\n      charCount: cleanedText.length\r\n    }, null, 2);\r\n  }\r\n\r\n  formatTranscripts(transcripts: FetchedTranscript[]): string {\r\n    return transcripts.map(t => this.formatTranscript(t)).join('\\n\\n');\r\n  }\r\n}\r\n\r\n/**\r\n * Base class for subtitle formatters (SRT, WebVTT).\r\n */\r\nabstract class SubtitleFormatterBase extends TextFormatter {\r\n  protected abstract formatTimestamp(h: number, m: number, s: number, ms: number): string;\r\n  protected abstract formatTranscriptHeader(lines: string[]): string;\r\n  protected abstract formatSubtitleEntry(index: number, timeRange: string, snippet: FetchedTranscriptSnippet): string;\r\n\r\n  protected secondsToTimestamp(timeInSeconds: number): string {\r\n    const hours = Math.floor(timeInSeconds / 3600);\r\n    const remainder = timeInSeconds % 3600;\r\n    const minutes = Math.floor(remainder / 60);\r\n    const seconds = Math.floor(remainder % 60);\r\n    const milliseconds = Math.round((timeInSeconds - Math.floor(timeInSeconds)) * 1000);\r\n\r\n    return this.formatTimestamp(hours, minutes, seconds, milliseconds);\r\n  }\r\n\r\n  formatTranscript(transcript: FetchedTranscript): string {\r\n    const formattedLines: string[] = [];\r\n\r\n    for (let i = 0; i < transcript.snippets.length; i++) {\r\n      const snippet = transcript.snippets[i];\r\n      const snippetEndTime = snippet.start + snippet.duration;\r\n\r\n      const actualEndTime =\r\n        i < transcript.snippets.length - 1 && transcript.snippets[i + 1].start < snippetEndTime\r\n          ? transcript.snippets[i + 1].start\r\n          : snippetEndTime;\r\n\r\n      const timeRangeText = `${this.secondsToTimestamp(snippet.start)} --> ${this.secondsToTimestamp(actualEndTime)}`;\r\n\r\n      formattedLines.push(this.formatSubtitleEntry(i, timeRangeText, snippet));\r\n    }\r\n\r\n    return this.formatTranscriptHeader(formattedLines);\r\n  }\r\n}\r\n\r\n/**\r\n * Formatter that converts transcripts to SRT (SubRip) format.\r\n */\r\nexport class SRTFormatter extends SubtitleFormatterBase {\r\n  protected formatTimestamp(h: number, m: number, s: number, ms: number): string {\r\n    const pad = (n: number, w: number) => n.toString().padStart(w, '0');\r\n    return `${pad(h, 2)}:${pad(m, 2)}:${pad(s, 2)},${pad(ms, 3)}`;\r\n  }\r\n\r\n  protected formatTranscriptHeader(lines: string[]): string {\r\n    return lines.join('\\n\\n') + '\\n';\r\n  }\r\n\r\n  protected formatSubtitleEntry(index: number, timeRange: string, snippet: FetchedTranscriptSnippet): string {\r\n    return `${index + 1}\\n${timeRange}\\n${snippet.text}`;\r\n  }\r\n}\r\n\r\n/**\r\n * Formatter that converts transcripts to WebVTT format.\r\n */\r\nexport class WebVTTFormatter extends SubtitleFormatterBase {\r\n  protected formatTimestamp(h: number, m: number, s: number, ms: number): string {\r\n    const pad = (n: number, w: number) => n.toString().padStart(w, '0');\r\n    return `${pad(h, 2)}:${pad(m, 2)}:${pad(s, 2)}.${pad(ms, 3)}`;\r\n  }\r\n\r\n  protected formatTranscriptHeader(lines: string[]): string {\r\n    return 'WEBVTT\\n\\n' + lines.join('\\n\\n') + '\\n';\r\n  }\r\n\r\n  protected formatSubtitleEntry(index: number, timeRange: string, snippet: FetchedTranscriptSnippet): string {\r\n    return `${timeRange}\\n${snippet.text}`;\r\n  }\r\n}\r\n\r\n/**\r\n * Raised when an unknown formatter type is requested.\r\n */\r\nexport class UnknownFormatterType extends Error {\r\n  constructor(formatterType: string) {\r\n    super(`Unknown formatter type: ${formatterType}. Available types: json, text, srt, webvtt, pretty, article`);\r\n    this.name = 'UnknownFormatterType';\r\n    Object.setPrototypeOf(this, UnknownFormatterType.prototype);\r\n  }\r\n}\r\n\r\n/**\r\n * Utility class for loading formatters by type string.\r\n */\r\nexport class FormatterLoader {\r\n  load(formatterType?: string): Formatter {\r\n    const type = formatterType?.toLowerCase() || 'pretty';\r\n\r\n    switch (type) {\r\n      case 'json':\r\n        return new JSONFormatter();\r\n      case 'text':\r\n        return new TextFormatter();\r\n      case 'article':\r\n        return new ArticleFormatter();\r\n      case 'srt':\r\n        return new SRTFormatter();\r\n      case 'webvtt':\r\n        return new WebVTTFormatter();\r\n      case 'pretty':\r\n        return new PrettyPrintFormatter();\r\n      default:\r\n        throw new UnknownFormatterType(type);\r\n    }\r\n  }\r\n}\r\n"],"x_google_ignoreList":[9,10,11],"mappings":"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"}