{"version":3,"sources":["../src/index.ts","../src/Downloader.ts","../src/FormatConverter.ts","../src/utils.ts","../src/SongTagsSearch.ts","../src/YouTubeSession.ts","../src/SignatureDecryptor.ts","../src/cookies.json","../src/SessionManager.ts","../src/main.ts"],"sourcesContent":["export { Downloader, DownloaderOptions } from './Downloader';\nexport { FormatConverter } from './FormatConverter';\nexport { YouTubeSession, YouTubeSessionOptions, VideoInfo, VideoFormat, ProxyConfig } from './YouTubeSession';\nexport { SessionManager, SessionConfig } from './SessionManager';\nexport * from './SongTagsSearch';\nexport { main } from './main';\nexport { YtdlMp3Error } from './utils';\n","// src/downloader/Downloader.ts\nimport os from 'os';\nimport path from 'path';\nimport NodeID3 from 'node-id3';\nimport { FormatConverter } from './FormatConverter';\nimport { SongTagsSearch } from './SongTagsSearch';\nimport { YouTubeSession, YouTubeSessionOptions, VideoInfo, VideoFormat, ProxyConfig } from './YouTubeSession';\nimport { isDirectory, removeParenthesizedText, YtdlMp3Error } from './utils';\nimport fs from 'fs';\nimport myCookie from './cookies.json';\nimport { Cookie } from '@distube/ytdl-core';\n\nexport interface DownloaderOptions {\n  outputDir?: string;\n  getTags?: boolean;\n  verifyTags?: boolean;\n  cookies?: Cookie[];\n  retries?: number;\n  timeout?: number;\n  forceCloudMode?: boolean; // Force cloud function optimizations\n  proxy?: ProxyConfig | string; // Residential proxy for anti-bot protection\n}\n\nexport class Downloader {\n  static defaultDownloadsDir = path.join(os.homedir(), 'Downloads');\n  private youtubeSession: YouTubeSession;\n  private options: DownloaderOptions;\n\n  constructor(options: DownloaderOptions = {}) {\n    this.options = {\n      getTags: true,\n      verifyTags: false,\n      retries: 3,\n      timeout: 30000,\n      cookies: myCookie as Cookie[],\n      ...options\n    };\n\n    // Validate cookies\n    if (!this.options.cookies || this.options.cookies.length === 0) {\n      throw new YtdlMp3Error('No cookies provided. Please add YouTube cookies for authentication.');\n    }\n\n    // Initialize YouTube session with cookies, cloud mode, and proxy options\n    console.log('🔧 Initializing YouTube session...');\n    const sessionOptions: YouTubeSessionOptions = {\n      cookies: this.options.cookies!,\n      ...(this.options.forceCloudMode !== undefined && { forceCloudMode: this.options.forceCloudMode }),\n      ...(this.options.proxy && { proxy: this.options.proxy })\n    };\n\n    this.youtubeSession = new YouTubeSession(sessionOptions);\n    \n    // Log session health\n    const isAuth = this.youtubeSession.isAuthenticated();\n    const cookieCount = this.youtubeSession.getCookieCount();\n    \n    console.log(`📊 Session status: ${isAuth ? '✅ Authenticated' : '⚠️  Not authenticated'}`);\n    console.log(`🍪 Total cookies: ${cookieCount}`);\n  }\n\n  async downloadSong(url: string, customTitle?: string): Promise<string> {\n    const maxRetries = this.options.retries || 3;\n    let lastError: Error;\n\n    for (let attempt = 1; attempt <= maxRetries; attempt++) {\n      try {\n        console.log(`🔄 Download attempt ${attempt}/${maxRetries} for: ${url}`);\n        return await this.performDownload(url, customTitle);\n      } catch (error) {\n        lastError = error as Error;\n        console.warn(`❌ Attempt ${attempt} failed:`, error);\n        \n        if (attempt < maxRetries) {\n          const backoffMs = Math.min(1000 * Math.pow(2, attempt - 1), 10000);\n          console.log(`⏳ Retrying in ${backoffMs}ms...`);\n          await this.sleep(backoffMs);\n        }\n      }\n    }\n\n    throw new YtdlMp3Error(`Download failed after ${maxRetries} attempts: ${lastError!.message}`);\n  }\n\n  private async performDownload(url: string, customTitle?: string): Promise<string> {\n    const { outputDir, getTags, verifyTags } = this.options;\n    const validOutputDir = outputDir || Downloader.defaultDownloadsDir;\n\n    if (!isDirectory(validOutputDir)) {\n      throw new YtdlMp3Error(`Not a directory: ${validOutputDir}`);\n    }\n\n    // Get video information\n    console.log('📡 Fetching video information...');\n    const videoInfo = await this.youtubeSession.getVideoInfo(url);\n    const songTitle = customTitle || videoInfo.title;\n\n    console.log(`🎵 Title: ${songTitle}`);\n    console.log(`👤 Author: ${videoInfo.author}`);\n    console.log(`⏱️  Duration: ${videoInfo.lengthSeconds}s`);\n\n    // Select best audio format\n    const audioFormat = this.youtubeSession.getBestAudioFormat(videoInfo.formats);\n    if (!audioFormat) {\n      throw new YtdlMp3Error('No suitable audio format found');\n    }\n\n    console.log(`🎧 Selected format: itag ${audioFormat.itag}, quality: ${audioFormat.audioQuality}`);\n\n    // Download audio stream\n    console.log('⬇️  Downloading audio stream...');\n    const audioData = await this.downloadWithProgress(url, audioFormat);\n\n    // Convert to MP3\n    console.log('🔄 Converting to MP3...');\n    const formatConverter = new FormatConverter();\n    const outputFile = this.getOutputFile(songTitle, validOutputDir);\n\n    formatConverter.videoToAudio(audioData, outputFile);\n\n    // Add ID3 tags if requested\n    if (getTags) {\n      console.log('🏷️  Adding ID3 tags...');\n      try {\n        // Always use the original YouTube video title for iTunes search, not custom filename\n        const songTagsSearch = new SongTagsSearch(videoInfo.title);\n        const songTags = await songTagsSearch.search(verifyTags);\n        NodeID3.write(songTags, outputFile);\n        console.log('✅ ID3 tags added successfully');\n      } catch (tagError) {\n        console.warn('⚠️  Failed to add ID3 tags:', tagError);\n        // Don't fail the entire download for tag errors\n      }\n    }\n\n    console.log(`🎉 Download completed: ${outputFile}`);\n    return outputFile;\n  }\n\n  private async downloadWithProgress(url: string, format: VideoFormat): Promise<Buffer> {\n    const startTime = Date.now();\n    const expectedSize = parseInt(format.contentLength) || 0;\n    \n    try {\n      const audioData = await this.youtubeSession.downloadAudioStream(url);\n      const downloadTime = Date.now() - startTime;\n      const sizeMB = (audioData.length / 1024 / 1024).toFixed(2);\n      \n      console.log(`✅ Downloaded ${sizeMB}MB in ${downloadTime}ms`);\n      \n      if (expectedSize > 0 && Math.abs(audioData.length - expectedSize) > expectedSize * 0.1) {\n        console.warn(`⚠️  Size mismatch: expected ${expectedSize}, got ${audioData.length}`);\n      }\n      \n      return audioData;\n    } catch (error) {\n      throw new YtdlMp3Error(`Download failed: ${error}`);\n    }\n  }\n\n  private getOutputFile(videoTitle: string, outputDir: string): string {\n    const baseFileName = removeParenthesizedText(videoTitle)\n      .replace(/[^a-z0-9\\s-_]/gi, '')\n      .replace(/\\s+/g, '_')\n      .toLowerCase()\n      .substring(0, 100); // Limit filename length\n\n    let fileName = baseFileName + '.mp3';\n    let suffix = 1;\n\n    while (fs.existsSync(path.join(outputDir, fileName))) {\n      fileName = `${baseFileName}_${suffix}.mp3`;\n      suffix++;\n    }\n\n    return path.join(outputDir, fileName);\n  }\n\n  private sleep(ms: number): Promise<void> {\n    return new Promise(resolve => setTimeout(resolve, ms));\n  }\n\n\n\n  // Utility method to test connection\n  async testConnection(url: string): Promise<boolean> {\n    try {\n      const videoInfo = await this.youtubeSession.getVideoInfo(url);\n      console.log(`✅ Connection test successful: ${videoInfo.title}`);\n      return true;\n    } catch (error) {\n      console.error(`❌ Connection test failed:`, error);\n      return false;\n    }\n  }\n\n  // Utility method to get video info without downloading\n  async getVideoInfo(url: string): Promise<VideoInfo> {\n    return await this.youtubeSession.getVideoInfo(url);\n  }\n\n  // Session health check\n  getSessionHealth(): {\n    isAuthenticated: boolean;\n    cookieCount: number;\n    hasValidCookies: boolean;\n  } {\n    const now = Date.now() / 1000;\n    const validCookies = this.options.cookies!.filter(cookie => \n      !cookie.expirationDate || cookie.expirationDate > now\n    );\n\n    return {\n      isAuthenticated: this.youtubeSession.isAuthenticated(),\n      cookieCount: this.youtubeSession.getCookieCount(),\n      hasValidCookies: validCookies.length > 0\n    };\n  }\n}\n","import cp from 'child_process';\nimport fs from 'fs';\n\nimport ffmpeg from 'ffmpeg-static';\n\nimport { YtdlMp3Error } from './utils';\n\nexport class FormatConverter {\n  private readonly ffmpegBinary: string;\n\n  constructor() {\n    if (!ffmpeg) {\n      throw new YtdlMp3Error('Failed to resolve ffmpeg binary');\n    }\n    console.log('ffmpeg binary path:', ffmpeg); // Add this line to verify path\n    this.ffmpegBinary = ffmpeg;\n  }\n\n  videoToAudio(videoData: Buffer, outputFile: string): void {\n    if (fs.existsSync(outputFile)) {\n      throw new YtdlMp3Error(`Output file already exists: ${outputFile}`);\n    }\n    // cp.execSync(`${this.ffmpegBinary} -loglevel 24 -i pipe:0 -vn -sn -c:a mp3 -ab 192k ${outputFile}`, {\n    //   input: videoData,\n    // });\n    try {\n      cp.execSync(`${this.ffmpegBinary} -loglevel 24 -i pipe:0 -vn -sn -c:a mp3 -ab 192k ${outputFile}`, {\n        input: new Uint8Array(videoData.buffer, videoData.byteOffset, videoData.byteLength),\n      });\n    } catch (error: any) {\n      console.error('Failed to execute ffmpeg command:', error); // Debugging line\n      throw new YtdlMp3Error(`ffmpeg command failed: ${error.message}`);\n    }\n  }\n}\n\n// import cp from 'child_process';\n// import fs from 'fs';\n// import ffmpeg from 'ffmpeg-static';\n// import { YtdlMp3Error } from './utils';\n\n// export class FormatConverter {\n//   private readonly ffmpegBinary: string;\n\n//   constructor() {\n//     if (!ffmpeg) {\n//       throw new YtdlMp3Error('Failed to resolve ffmpeg binary');\n//     }\n//     this.ffmpegBinary = ffmpeg;\n//   }\n\n//   async videoToAudio(videoData: Buffer, outputFile: string): Promise<string> {\n//     // ... (input validation checks)\n\n//     if (!this.ffmpegBinary) {\n//       throw new YtdlMp3Error('Failed to resolve ffmpeg binary');\n//     }\n\n//     const ffmpegOptions = ['-loglevel', '24', '-i', 'pipe:0', '-vn', '-sn', '-c:a', 'mp3', '-ab', '256k', outputFile];\n\n//     try {\n//       const ffmpegProcess = cp.spawn(this.ffmpegBinary, ffmpegOptions);\n\n//       ffmpegProcess.stdin.write(videoData);\n//       ffmpegProcess.stdin.end();\n\n//       await new Promise<void>((resolve, reject) => {\n//         // Corrected Promise type\n//         ffmpegProcess.on('exit', (code) => {\n//           if (code !== 0) {\n//             reject(new YtdlMp3Error(`FFmpeg process exited with code ${code}`));\n//           } else {\n//             resolve(); // No need to pass a value when resolving with void\n//           }\n//         });\n//         ffmpegProcess.on('error', (err) => reject(new YtdlMp3Error(`Error during FFmpeg spawn: ${err.message}`)));\n//       });\n\n//       // Ensure file exists and has a valid size before returning\n//       if (!fs.existsSync(outputFile)) {\n//         throw new YtdlMp3Error(`Output file not created: ${outputFile}`);\n//       }\n//       const fileSize = fs.statSync(outputFile).size;\n//       if (fileSize <= 0) {\n//         throw new YtdlMp3Error(`Output file has invalid size: ${fileSize}`);\n//       }\n\n//       console.log('Conversion completed successfully.');\n//       return outputFile; // Return the output file path\n//     } catch (error: any) {\n//       throw new YtdlMp3Error(`Error during FFmpeg execution: ${error.message}`);\n//     }\n//   }\n// }\n\n// import cp from 'child_process';\n// import fs from 'fs';\n// import ffmpeg from 'ffmpeg-static';\n// import util from 'util';\n// import { YtdlMp3Error } from './utils';\n\n// const promisifyOn = (emitter: NodeJS.EventEmitter, event: string) => util.promisify(emitter.on).call(emitter, event);\n\n// export class FormatConverter {\n//   private readonly ffmpegBinary: string;\n\n//   constructor() {\n//     if (!ffmpeg) {\n//       throw new YtdlMp3Error('Failed to resolve ffmpeg binary');\n//     }\n//     this.ffmpegBinary = ffmpeg;\n//   }\n\n//   async videoToAudio(videoData: Buffer, outputFile: string): Promise<string> {\n//     // ... (input validation checks)\n\n//     if (!this.ffmpegBinary) {\n//       throw new YtdlMp3Error('Failed to resolve ffmpeg binary');\n//     }\n\n//     const ffmpegOptions = ['-loglevel', '24', '-i', 'pipe:0', '-vn', '-sn', '-c:a', 'mp3', '-ab', '320k', outputFile];\n\n//     try {\n//       const ffmpegProcess = cp.spawn(this.ffmpegBinary, ffmpegOptions);\n\n//       const writePromise = util.promisify((data: Buffer) => ffmpegProcess.stdin.write(data))(videoData);\n//       const endPromise = util.promisify(ffmpegProcess.stdin.end).call(ffmpegProcess.stdin);\n\n//       await Promise.all([writePromise, endPromise]);\n\n//       const [exitCode] = await Promise.race([\n//         promisifyOn(ffmpegProcess, 'exit'),\n//         promisifyOn(ffmpegProcess, 'error').then((err: any) => Promise.reject(err)),\n//       ]);\n\n//       if (exitCode !== 0) {\n//         throw new YtdlMp3Error(`FFmpeg process exited with code ${exitCode}`);\n//       }\n\n//       if (!fs.existsSync(outputFile)) {\n//         throw new YtdlMp3Error(`Output file not created: ${outputFile}`);\n//       }\n\n//       const fileSize = fs.statSync(outputFile).size;\n//       if (fileSize <= 0) {\n//         throw new YtdlMp3Error(`Output file has an invalid size: ${fileSize}`);\n//       }\n\n//       console.log('Conversion completed successfully.');\n//       return outputFile;\n//     } catch (error: any) {\n//       throw new YtdlMp3Error(`Error during FFmpeg execution: ${error.message}`);\n//     }\n//   }\n// }\n","import fs from 'fs';\r\nimport readline from 'readline';\r\n\r\nexport function removeParenthesizedText(s: string): string {\r\n  // Remove characters within parentheses or square brackets\r\n  let result = s.replace(/\\([^)]*\\)|\\[[^\\]]*\\]/g, '');\r\n\r\n  const removeAmp = (str: string) => {\r\n    return str.replace(/&amp;/g, '&');\r\n  };\r\n  // Remove \"amp;\" before further processing\r\n  result = removeAmp(result);\r\n\r\n  const removeQuot = (str: string) => {\r\n    return str.replace(/quot;/g, '&');\r\n  };\r\n\r\n  // Remove \"amp;\" before further processing\r\n  result = removeQuot(result);\r\n\r\n  const removeAfterSkizaAndNumber = (str: string) => {\r\n    return str.replace(/Skiza \\d+.*$/gi, '');\r\n  };\r\n  // Remove \"Skiza\" before further processing\r\n  result = removeAfterSkizaAndNumber(result);\r\n\r\n  // Remove the part starting from \"SMS\" to the end\r\n  result = result.replace(/SMS.*$/g, '');\r\n  // Remove specific words and characters\r\n  const wordsToRemove = [\r\n    'Lyrics',\r\n    'lyrics',\r\n    'Feat.',\r\n    'feat.',\r\n    'Ft.',\r\n    'ft.',\r\n    'audio',\r\n    'Audio',\r\n    'Live',\r\n    'live',\r\n    'Video',\r\n    'video',\r\n    '|',\r\n  ];\r\n\r\n  // Use a regular expression to globally replace the words with an empty string\r\n  wordsToRemove.forEach((word) => {\r\n    result = result.replace(new RegExp(`\\\\b${word.replace('.', '\\\\.?')}\\\\b`, 'gi'), '');\r\n  });\r\n\r\n  // Remove words starting with '#'\r\n  result = result.replace(/#\\w+/g, '');\r\n\r\n  // Remove standalone periods, but not those next to a word character\r\n  result = result.replace(/(?<!\\w)\\.(?!\\w)/g, '');\r\n\r\n  // Remove consecutive spaces\r\n  result = result.replace(/\\s+/g, ' ');\r\n\r\n  // // Remove occurrences of the character \"x\" with spaces before and after\r\n  // result = result.replace(/\\s*x\\s*/g, '');\r\n\r\n  // Remove occurrences of the character \"x\" with spaces before and after\r\n  result = result.replace(/\\s+x\\s+/g, ' ');\r\n\r\n  // Remove quotes and special characters\r\n  result = result.replace(/['\"!@#$%^&*(),.?\":{}|<>]/g, '');\r\n\r\n  // Trim leading and trailing spaces\r\n  result = result.trim();\r\n\r\n  return result;\r\n}\r\n\r\nexport function isDirectory(dirPath: string): boolean {\r\n  return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory();\r\n}\r\n\r\nexport async function userInput(prompt: string, defaultInput?: string): Promise<string> {\r\n  const rl = readline.createInterface({\r\n    input: process.stdin,\r\n    output: process.stdout,\r\n  });\r\n\r\n  return new Promise((resolve, reject) => {\r\n    rl.question(prompt, (response) => {\r\n      rl.close();\r\n      if (response) {\r\n        resolve(response);\r\n      } else {\r\n        reject(new YtdlMp3Error('Invalid response: ' + response));\r\n      }\r\n    });\r\n    rl.write(defaultInput || '');\r\n  });\r\n}\r\n\r\nexport class YtdlMp3Error extends Error {\r\n  constructor(message: string, options?: ErrorOptions) {\r\n    super(message, options);\r\n    this.name = 'YtdlMp3Error';\r\n  }\r\n}\r\n","import axios, { type AxiosError } from 'axios';\nimport type { MoreVideoDetails } from '@distube/ytdl-core';\n\nimport { YtdlMp3Error, removeParenthesizedText, userInput } from './utils';\n\nexport interface SearchResult {\n  artistName: string;\n  trackName: string;\n  artworkUrl100: string;\n  artworkUrl: string;\n  trackCount: number;\n  trackId: number;\n  discNumber: number;\n  trackNumber: string;\n  collectionId: number;\n  collectionName: string;\n  releaseDate: string;\n  primaryGenreName: string;\n  artistViewUrl: string;\n  generalObject: Array<{}>;\n  // Enhanced metadata fields\n  trackTimeMillis?: number;\n  trackExplicitness?: string;\n  collectionExplicitness?: string;\n  artistId?: number;\n  discCount?: number;\n  previewUrl?: string;\n  trackViewUrl?: string;\n  collectionViewUrl?: string;\n  trackCensoredName?: string;\n  collectionCensoredName?: string;\n  country?: string;\n  currency?: string;\n  trackPrice?: number;\n  collectionPrice?: number;\n  isStreamable?: boolean;\n}\n\nexport interface SearchData {\n  resultCount: number;\n  results: SearchResult[];\n}\n\nexport interface AlbumArt {\n  mime: string;\n  type: {\n    id: number;\n    name: string;\n  };\n  description: string;\n  imageBuffer: Buffer;\n}\n\nexport interface SongTags {\n  title: string;\n  artist: string;\n  originalArtist: string;\n  image: AlbumArt;\n  trackCount: number;\n  trackId: number;\n  discNumber: number;\n  trackNumber: string;\n  albumId: number;\n  album: string;\n  genre: string;\n  encodingTime: string;\n  releaseTime: string;\n  originalYear: string;\n  year: string;\n  artistUrl: Array<string>;\n  \n  // Enhanced metadata (node-id3 compatible)\n  length?: string;          // Duration in MM:SS format (from trackTimeMillis)\n  comment?: {               // Comment in node-id3 format\n    language: string;\n    text: string;\n  };\n  composer?: string;        // Additional artist info\n  encodedBy?: string;       // Source information\n  \n  generalObject: Array<{\n    mimeType?: string;\n    /**\n     * Case sensitive\n     */\n    filename?: string;\n    /**\n     * There may be more than one \"GEOB\" frame in each tag, but only\n     * one with the same content description.\n     */\n    contentDescription: string;\n    encapsulatedObject: Buffer;\n  }>;\n}\n\nexport class SongTagsSearch {\n  private searchTerm: string;\n  private url: URL;\n\n  constructor(songTitle: string) {\n    this.searchTerm = removeParenthesizedText(songTitle);\n    this.url = new URL('https://itunes.apple.com/search?');\n    this.url.searchParams.set('media', 'music');\n    this.url.searchParams.set('term', this.searchTerm);\n  }\n\n  async search(verify = false): Promise<SongTags> {\n    console.log(`Attempting to query iTunes API with the following search term: ${this.searchTerm}`);\n    const searchResults = await this.fetchResults();\n    // Always use smart matching to pick the best result\n    const result = await this.getSmartMatchedResult(searchResults, this.searchTerm);\n    // Fetch album art once, potentially in parallel with other tasks\n    const albumArt = await this.fetchAlbumArt(\n      this.getHighestQualityArtworkUrl(result)\n    );\n    const songYear = await this.processDate(result?.releaseDate);\n    \n    return {\n      // Basic tags\n      title: result.trackName,\n      artist: result.artistName,\n      trackCount: result.trackCount,\n      trackId: result.trackId,\n      discNumber: result.discNumber,\n      trackNumber: result.trackNumber,\n      albumId: result.collectionId,\n      originalArtist: result?.artistName,\n      album: result?.collectionName,\n      genre: result?.primaryGenreName,\n      encodingTime: result?.releaseDate,\n      releaseTime: result?.releaseDate,\n      originalYear: songYear,\n      year: songYear,\n      artistUrl: [result?.artistViewUrl],\n      \n      // Enhanced metadata\n      length: this.formatDuration(result.trackTimeMillis),\n      comment: this.buildEnhancedComment(result),\n      composer: this.getComposerInfo(result),\n      encodedBy: 'audiodl-mp3 via iTunes API',\n      \n      generalObject: [\n        {\n          mimeType: undefined,\n          filename: undefined,\n          contentDescription: `${result?.artistName} - ${result.trackName}`,\n          encapsulatedObject: Buffer.alloc(0),\n        },\n      ],\n      image: {\n        mime: 'image/png',\n        type: {\n          id: 3,\n          name: 'front cover',\n        },\n        description: 'Album Art',\n        imageBuffer: albumArt,\n      },\n    };\n  }\n\n  private async fetchResults(): Promise<SearchResult[]> {\n    try {\n      const response = await axios.get<SearchData>(this.url.href);\n\n      if (response.data.resultCount === 0) {\n        throw new YtdlMp3Error('Call to iTunes API did not return any results');\n      }\n\n      return response.data.results;\n    } catch (itunesError) {\n      console.error('Error calling iTunes API:', itunesError);\n\n      try {\n        // Attempt to fetch data from Shazam\n        const shazamData = await this.fetchShazamData(this.searchTerm);\n        return [shazamData];\n      } catch (shazamError) {\n        // If both iTunes and Shazam API requests fail, throw an error\n        console.error('Error calling Shazam API:', shazamError);\n        throw new YtdlMp3Error('Failed to fetch results from both iTunes and Shazam APIs');\n      }\n    }\n  }\n\n  /**\n   * Parse YouTube title into artist and song title components\n   */\n  private parseYouTubeTitle(title: string): { artist: string; track: string } {\n    // Common patterns in YouTube titles\n    const patterns = [\n      /^(.+?)\\s*-\\s*(.+)$/,           // \"Artist - Song Title\"\n      /^(.+?)\\s*by\\s+(.+)$/i,        // \"Song Title by Artist\"\n      /^(.+?)\\s*\\|\\s*(.+)$/,         // \"Artist | Song Title\"\n      /^(.+?)\\s*:\\s*(.+)$/,          // \"Artist: Song Title\"\n    ];\n\n    for (const pattern of patterns) {\n      const match = title.match(pattern);\n      if (match) {\n        const [, part1, part2] = match;\n        // For \"by\" pattern, swap the order\n        if (pattern.source.includes('by')) {\n          return { artist: part2.trim(), track: part1.trim() };\n        }\n        return { artist: part1.trim(), track: part2.trim() };\n      }\n    }\n\n    // If no pattern matches, treat the whole thing as track title\n    return { artist: '', track: title };\n  }\n\n  /**\n   * Smart matching algorithm that scores iTunes results based on artist and track similarity\n   */\n  private async getSmartMatchedResult(searchResults: SearchResult[], searchTerm: string): Promise<SearchResult> {\n    const parsed = this.parseYouTubeTitle(searchTerm);\n    console.log(`🎯 Parsed YouTube title: Artist=\"${parsed.artist}\", Track=\"${parsed.track}\"`);\n\n    let bestMatch: SearchResult | null = null;\n    let bestScore = 0;\n\n    for (const result of searchResults) {\n      console.log(`\\n🔍 Checking iTunes result:`);\n      console.log(`   Artist: \"${result.artistName}\"`);\n      console.log(`   Track: \"${result.trackName}\"`);\n\n      let score = 0;\n\n      // Score artist match (40% weight)\n      if (parsed.artist) {\n        const artistScore = this.calculateSimilarity(parsed.artist, result.artistName);\n        score += artistScore * 0.4;\n        console.log(`   Artist similarity: ${(artistScore * 100).toFixed(1)}%`);\n      }\n\n      // Score track match (60% weight)\n      const trackScore = this.calculateSimilarity(parsed.track, result.trackName);\n      score += trackScore * 0.6;\n      console.log(`   Track similarity: ${(trackScore * 100).toFixed(1)}%`);\n      console.log(`   Total score: ${(score * 100).toFixed(1)}%`);\n\n      if (score > bestScore) {\n        bestScore = score;\n        bestMatch = result;\n      }\n    }\n\n    if (bestMatch && bestScore > 0.3) { // Require at least 30% similarity\n      console.log(`\\n✅ Best match selected (${(bestScore * 100).toFixed(1)}% confidence):`);\n      console.log(`   Artist: \"${bestMatch.artistName}\"`);\n      console.log(`   Track: \"${bestMatch.trackName}\"`);\n      return bestMatch;\n    }\n\n    // Fallback to first result if no good match\n    console.log(`\\n⚠️  No strong match found, using first result`);\n    return searchResults[0];\n  }\n\n  /**\n   * Calculate similarity between two strings (0-1 scale)\n   */\n  private calculateSimilarity(str1: string, str2: string): number {\n    if (!str1 || !str2) return 0;\n\n    const s1 = str1.toLowerCase().trim();\n    const s2 = str2.toLowerCase().trim();\n\n    // Exact match\n    if (s1 === s2) return 1.0;\n\n    // Check if one contains the other\n    if (s1.includes(s2) || s2.includes(s1)) return 0.8;\n\n    // Word-based similarity\n    const words1 = s1.split(/\\s+/);\n    const words2 = s2.split(/\\s+/);\n    \n    let matchingWords = 0;\n    const totalWords = Math.max(words1.length, words2.length);\n\n    for (const word1 of words1) {\n      if (word1.length < 3) continue; // Skip short words\n      for (const word2 of words2) {\n        if (word1 === word2 || word1.includes(word2) || word2.includes(word1)) {\n          matchingWords++;\n          break;\n        }\n      }\n    }\n\n    return matchingWords / totalWords;\n  }\n\n\n\n  /**\n   * Get the highest quality artwork URL available from iTunes\n   */\n  private getHighestQualityArtworkUrl(result: SearchResult): string {\n    // Try different qualities in order of preference (highest to lowest)\n    const qualityLevels = [\n      '3000x3000bb.jpg',  // Ultra-high quality (3K)\n      '1400x1400bb.jpg',  // High quality (1.4K) \n      '1000x1000bb.jpg',  // Good quality (1K)\n      '600x600bb.jpg',    // Medium quality (600px)\n      '300x300bb.jpg',    // Low quality (300px)\n    ];\n    \n    // Start with artworkUrl100 if available, otherwise use artworkUrl\n    let baseUrl = '';\n    if (result.artworkUrl100 && result.artworkUrl100.length > 0) {\n      baseUrl = result.artworkUrl100;\n    } else if (result.artworkUrl && result.artworkUrl.length > 0) {\n      baseUrl = result.artworkUrl;\n    } else {\n      console.warn('⚠️  No artwork URL available');\n      return '';\n    }\n    \n    // Replace with highest quality first\n    for (const quality of qualityLevels) {\n      const highQualityUrl = baseUrl.replace('100x100bb.jpg', quality);\n      if (highQualityUrl !== baseUrl) {\n        console.log(`🎨 Using ultra-high quality artwork: ${quality}`);\n        return highQualityUrl;\n      }\n    }\n    \n    // Fallback to original URL if no replacement was possible\n    console.log(`🎨 Using original artwork URL`);\n    return baseUrl;\n  }\n\n  private async fetchAlbumArt(url: string): Promise<Buffer> {\n    if (!url || url.length === 0) {\n      throw new YtdlMp3Error('No artwork URL provided');\n    }\n    \n    return axios\n      .get(url, { responseType: 'arraybuffer' })\n      .then((response) => Buffer.from(response.data as string, 'binary'))\n      .catch((error) => {\n        console.warn(`⚠️  Failed to fetch artwork from: ${url}`, (error as Error).message);\n        throw new YtdlMp3Error('Failed to fetch album art from endpoint: ' + url);\n      });\n  }\n  private async processDate(dateString: string): Promise<string> {\n    try {\n      const year = dateString.slice(0, 4); // Extract the first four characters (the year)\n      if (isNaN(Number(year))) {\n        throw new Error('Invalid year');\n      }\n      return year;\n    } catch (error) {\n      console.error(`Error extracting year from date: ${dateString}`, error);\n      return '';\n    }\n\n    // Rest of your processing logic with the year\n  }\n\n  /**\n   * Format duration from milliseconds to MM:SS format\n   */\n  private formatDuration(trackTimeMillis?: number): string | undefined {\n    if (!trackTimeMillis) return undefined;\n    \n    const totalSeconds = Math.floor(trackTimeMillis / 1000);\n    const minutes = Math.floor(totalSeconds / 60);\n    const seconds = totalSeconds % 60;\n    \n    return `${minutes}:${seconds.toString().padStart(2, '0')}`;\n  }\n\n  /**\n   * Build enhanced comment with iTunes metadata in node-id3 format\n   */\n  private buildEnhancedComment(result: SearchResult): { language: string; text: string } | undefined {\n    const parts = [];\n    \n    if (result.trackExplicitness) {\n      parts.push(`Content: ${result.trackExplicitness}`);\n    }\n    \n    if (result.country) {\n      parts.push(`Region: ${result.country}`);\n    }\n    \n    if (result.trackViewUrl) {\n      parts.push(`iTunes: ${result.trackViewUrl}`);\n    }\n    \n    if (result.isStreamable !== undefined) {\n      parts.push(`Streamable: ${result.isStreamable}`);\n    }\n    \n    if (parts.length > 0) {\n      return {\n        language: 'eng',\n        text: parts.join(' | ')\n      };\n    }\n    \n    return undefined;\n  }\n\n  /**\n   * Get composer/additional artist info\n   */\n  private getComposerInfo(result: SearchResult): string | undefined {\n    // If we have a censored name that's different, use it as composer info\n    if (result.trackCensoredName && result.trackCensoredName !== result.trackName) {\n      return result.trackCensoredName;\n    }\n    return undefined;\n  }\n\n  /**\n   * Get content rating information\n   */\n  private getContentRating(result: SearchResult): string | undefined {\n    if (result.trackExplicitness) {\n      return result.trackExplicitness === 'explicit' ? 'Explicit Content' : 'Clean';\n    }\n    return undefined;\n  }\n\n  /**\n   * Get publisher/label information\n   */\n  private getPublisherInfo(result: SearchResult): string | undefined {\n    const parts = [];\n    \n    if (result.currency && result.trackPrice) {\n      parts.push(`${result.currency} ${result.trackPrice}`);\n    }\n    \n    if (result.country) {\n      parts.push(result.country);\n    }\n    \n    return parts.length > 0 ? `iTunes Store (${parts.join(', ')})` : 'iTunes Store';\n  }\n\n  /**\n   * Build custom iTunes-specific tags\n   */\n  private buildCustomTags(result: SearchResult): Array<{ description: string; value: string }> {\n    const customTags = [];\n    \n    if (result.artistId) {\n      customTags.push({\n        description: 'iTunes Artist ID',\n        value: result.artistId.toString()\n      });\n    }\n    \n    if (result.trackId) {\n      customTags.push({\n        description: 'iTunes Track ID', \n        value: result.trackId.toString()\n      });\n    }\n    \n    if (result.collectionId) {\n      customTags.push({\n        description: 'iTunes Album ID',\n        value: result.collectionId.toString()\n      });\n    }\n    \n    if (result.previewUrl) {\n      customTags.push({\n        description: 'iTunes Preview URL',\n        value: result.previewUrl\n      });\n    }\n    \n    if (result.discCount && result.discCount > 1) {\n      customTags.push({\n        description: 'Total Discs',\n        value: result.discCount.toString()\n      });\n    }\n    \n    if (result.trackTimeMillis) {\n      customTags.push({\n        description: 'Duration (ms)',\n        value: result.trackTimeMillis.toString()\n      });\n    }\n    \n    return customTags;\n  }\n  private async fetchShazamData(cleanedString: string): Promise<SearchResult> {\n    const options = {\n      method: 'GET',\n      url: 'https://shazam.p.rapidapi.com/search',\n      params: {\n        term: cleanedString,\n        locale: 'en-US',\n        offset: '0',\n        limit: '5',\n      },\n      headers: {\n        'X-RapidAPI-Key': '1520cad159mshdb46120a994d8a9p11d921jsncc769f1cdf94',\n        'X-RapidAPI-Host': 'shazam.p.rapidapi.com',\n      },\n    };\n\n    try {\n      const response = await axios.request(options);\n      console.log(response.data.tracks.hits[0].track);\n      const resp2 = await axios.get(\n        `https://ws.audioscrobbler.com/2.0/?method=track.getInfo&api_key=508f1b6ceb2a4aecc3f4c15ad0494aaf&artist=${response.data.tracks.hits[0].track.subtitle}&track=${response.data.tracks.hits[0].track.title}&format=json`,\n      );\n      return {\n        artistName: response.data.tracks.hits[0].track.subtitle,\n        trackName: response.data.tracks.hits[0].track.title,\n        artworkUrl100: '',\n        artworkUrl: response.data.tracks.hits[0].track.images.coverarthq,\n        trackCount: 1,\n        trackId: Number(response.data.tracks.hits[0].track.key),\n        discNumber: Number(response.data.tracks.hits[0].track.key),\n        trackNumber: response.data.tracks.hits[0].track.layout,\n        collectionId: Number(response.data.tracks.hits[0].track.key),\n        collectionName: resp2.data.track.album.title,\n        releaseDate: '',\n        primaryGenreName: '',\n        artistViewUrl: '',\n        generalObject: response.data.tracks.hits[0].track.generalObject,\n      };\n    } catch (error) {\n      throw new YtdlMp3Error('Track info not found');\n    }\n  }\n}\n","import axios, { AxiosInstance } from 'axios';\r\nimport { Cookie } from '@distube/ytdl-core';\r\nimport ytdl from '@distube/ytdl-core';\r\nimport { YtdlMp3Error } from './utils';\r\nimport { SignatureDecryptor } from './SignatureDecryptor';\r\n\r\nexport interface ProxyConfig {\r\n  host: string;\r\n  port: number;\r\n  username?: string;\r\n  password?: string;\r\n}\r\n\r\nexport interface YouTubeSessionOptions {\r\n  cookies: Cookie[] | string;\r\n  userAgent?: string;\r\n  forceCloudMode?: boolean; // Manual override for cloud function detection\r\n  proxy?: ProxyConfig | string; // Proxy support for residential IPs\r\n}\r\n\r\nexport interface VideoFormat {\r\n  itag: number;\r\n  url: string;\r\n  mimeType: string;\r\n  bitrate: number;\r\n  audioQuality: string;\r\n  audioSampleRate: string;\r\n  audioChannels: number;\r\n  contentLength: string;\r\n  quality: string;\r\n  hasAudio: boolean;\r\n  hasVideo: boolean;\r\n}\r\n\r\nexport interface VideoInfo {\r\n  videoId: string;\r\n  title: string;\r\n  lengthSeconds: string;\r\n  formats: VideoFormat[];\r\n  author: string;\r\n  description: string;\r\n}\r\n\r\nexport class YouTubeSession {\r\n  private httpClient: AxiosInstance;\r\n  private cookies: string;\r\n  private visitorData: string;\r\n  private userAgent: string;\r\n  private isCloudFunction: boolean;\r\n  private requestDelay: number;\r\n  private proxyConfig?: ProxyConfig;\r\n\r\n  constructor(options: YouTubeSessionOptions) {\r\n    const cookieArray = Array.isArray(options.cookies) ? options.cookies : this.parseCookieString(options.cookies);\r\n    this.cookies = Array.isArray(options.cookies) ? this.formatCookies(options.cookies) : options.cookies;\r\n    this.visitorData = this.extractVisitorData(cookieArray);\r\n    this.isCloudFunction = options.forceCloudMode ?? this.detectCloudFunction();\r\n    this.userAgent = options.userAgent || this.getOptimalUserAgent();\r\n    this.requestDelay = this.isCloudFunction ? 3000 : 500; // Even longer delays for cloud functions\r\n    this.proxyConfig = this.parseProxyConfig(options.proxy);\r\n\r\n    // Create axios instance with proxy support\r\n    const axiosConfig: any = {\r\n      timeout: this.isCloudFunction ? 60000 : 30000, // Dynamic timeout based on environment\r\n      headers: this.buildOptimalHeaders(),\r\n    };\r\n\r\n    // Add proxy configuration if provided\r\n    if (this.proxyConfig) {\r\n      axiosConfig.proxy = {\r\n        host: this.proxyConfig.host,\r\n        port: this.proxyConfig.port,\r\n        auth: this.proxyConfig.username ? {\r\n          username: this.proxyConfig.username,\r\n          password: this.proxyConfig.password || ''\r\n        } : undefined\r\n      };\r\n    }\r\n\r\n    this.httpClient = axios.create(axiosConfig);\r\n\r\n    const cookieCount = Array.isArray(options.cookies) ? options.cookies.length : this.cookies.split(';').length;\r\n    console.log(`🔐 Session initialized with ${cookieCount} cookies`);\r\n    console.log(`☁️  Environment: ${this.isCloudFunction ? 'Cloud Function' : 'Local'}${options.forceCloudMode ? ' (forced)' : ''}`);\r\n    if (this.proxyConfig) {\r\n      console.log(`🌐 Proxy configured: ${this.proxyConfig.host}:${this.proxyConfig.port} ${this.proxyConfig.username ? '(authenticated)' : '(no auth)'}`);\r\n    }\r\n    if (this.visitorData) {\r\n      console.log(`📋 Visitor data extracted: ${this.visitorData.substring(0, 20)}...`);\r\n    }\r\n  }\r\n\r\n  private detectCloudFunction(): boolean {\r\n    // Detect common cloud function environments\r\n    const cloudIndicators = [\r\n      process.env.FUNCTION_NAME,        // Google Cloud Functions\r\n      process.env.AWS_LAMBDA_FUNCTION_NAME, // AWS Lambda\r\n      process.env.VERCEL,               // Vercel\r\n      process.env.NETLIFY,              // Netlify\r\n      process.env.RAILWAY_ENVIRONMENT,  // Railway\r\n      process.env.RENDER,               // Render\r\n      process.env.DYNO,                 // Heroku\r\n      process.env.CF_PAGES,             // Cloudflare Pages\r\n      process.env.DENO_DEPLOYMENT_ID,   // Deno Deploy\r\n      process.env.KUBERNETES_SERVICE_HOST, // Kubernetes\r\n      process.env.OPENSHIFT_BUILD_NAME, // OpenShift\r\n      process.env.FLY_APP_NAME,         // Fly.io\r\n      process.env.CONTAINER,            // Generic container\r\n      process.env.LAMBDA_RUNTIME_DIR,   // AWS Lambda (alternative)\r\n      process.env.GCLOUD_PROJECT,       // Google Cloud (alternative)\r\n      process.env.GAE_APPLICATION,      // Google App Engine\r\n    ];\r\n\r\n    // Check for explicit cloud function indicators\r\n    if (cloudIndicators.some(indicator => !!indicator)) {\r\n      return true;\r\n    }\r\n\r\n    // Additional heuristics for cloud/serverless environments\r\n    const serverlessHeuristics = [\r\n      // Check if running in a read-only filesystem (common in serverless)\r\n      !this.canWriteToTmp(),\r\n      // Check for limited environment variables (serverless typically has minimal env)\r\n      Object.keys(process.env).length < 50,\r\n      // Check for container-like hostnames\r\n      process.env.HOSTNAME?.startsWith('app') || false,\r\n      // Check for typical serverless memory limits\r\n      this.hasLimitedMemory(),\r\n      // Check for serverless-typical paths\r\n      __dirname.includes('/workspace') || __dirname.includes('/tmp') || __dirname.includes('/app'),\r\n      // Check for typical cloud function file structure\r\n      __dirname.includes('node_modules') && !__dirname.includes('Users') && !__dirname.includes('home'),\r\n    ];\r\n\r\n    // If 2 or more heuristics match, assume cloud function\r\n    const matchingHeuristics = serverlessHeuristics.filter(Boolean).length;\r\n    \r\n    return matchingHeuristics >= 2;\r\n  }\r\n\r\n  private canWriteToTmp(): boolean {\r\n    try {\r\n      const fs = require('fs');\r\n      const testFile = '/tmp/test-write-' + Date.now();\r\n      fs.writeFileSync(testFile, 'test');\r\n      fs.unlinkSync(testFile);\r\n      return true;\r\n    } catch {\r\n      return false;\r\n    }\r\n  }\r\n\r\n  private hasLimitedMemory(): boolean {\r\n    try {\r\n      const totalMem = process.memoryUsage().heapTotal;\r\n      // If less than 1GB of heap, likely serverless\r\n      return totalMem < 1024 * 1024 * 1024;\r\n    } catch {\r\n      return false;\r\n    }\r\n  }\r\n\r\n  private parseProxyConfig(proxy?: ProxyConfig | string): ProxyConfig | undefined {\r\n    if (!proxy) {\r\n      return undefined;\r\n    }\r\n\r\n    // If it's already a ProxyConfig object, return it\r\n    if (typeof proxy === 'object') {\r\n      return proxy;\r\n    }\r\n\r\n    // Parse string format: HOST:PORT:USER:PASS\r\n    try {\r\n      const parts = proxy.split(':');\r\n      if (parts.length < 2) {\r\n        throw new Error('Invalid proxy format. Expected HOST:PORT or HOST:PORT:USER:PASS');\r\n      }\r\n\r\n      const [host, portStr, username, password] = parts;\r\n      const port = parseInt(portStr, 10);\r\n\r\n      if (isNaN(port) || port <= 0 || port > 65535) {\r\n        throw new Error(`Invalid proxy port: ${portStr}`);\r\n      }\r\n\r\n      const config: ProxyConfig = {\r\n        host: host.trim(),\r\n        port,\r\n      };\r\n\r\n      if (username) {\r\n        config.username = username.trim();\r\n        config.password = password ? password.trim() : '';\r\n      }\r\n\r\n      return config;\r\n    } catch (error) {\r\n      console.warn(`⚠️  Failed to parse proxy config: ${error}`);\r\n      return undefined;\r\n    }\r\n  }\r\n\r\n  private getOptimalUserAgent(): string {\r\n    // More realistic user agents, rotated for cloud functions\r\n    const userAgents = [\r\n      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',\r\n      'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',\r\n      'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',\r\n      'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0',\r\n      'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15'\r\n    ];\r\n\r\n    // Use consistent user agent for same session but rotate across instances\r\n    const index = Math.floor(Date.now() / (1000 * 60 * 60)) % userAgents.length; // Change hourly\r\n    return userAgents[index];\r\n  }\r\n\r\n  private buildOptimalHeaders(): Record<string, string> {\r\n    const baseHeaders = {\r\n      'User-Agent': this.userAgent,\r\n      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',\r\n      'Accept-Language': 'en-US,en;q=0.9',\r\n      'Accept-Encoding': 'gzip, deflate, br',\r\n      'Cache-Control': 'no-cache',\r\n      'Pragma': 'no-cache',\r\n      'Sec-CH-UA': '\"Not A(Brand\";v=\"99\", \"Google Chrome\";v=\"121\", \"Chromium\";v=\"121\"',\r\n      'Sec-CH-UA-Mobile': '?0',\r\n      'Sec-CH-UA-Platform': '\"Windows\"',\r\n      'Sec-Fetch-Dest': 'document',\r\n      'Sec-Fetch-Mode': 'navigate',\r\n      'Sec-Fetch-Site': 'none',\r\n      'Sec-Fetch-User': '?1',\r\n      'Upgrade-Insecure-Requests': '1',\r\n      'Cookie': this.cookies,\r\n    };\r\n\r\n    // Add cloud-function specific headers\r\n    if (this.isCloudFunction) {\r\n      return {\r\n        ...baseHeaders,\r\n        ...this.getForwardedHeaders(),\r\n        'DNT': '1',\r\n        'Connection': 'keep-alive',\r\n      };\r\n    }\r\n\r\n    return {\r\n      ...baseHeaders,\r\n      'Origin': 'https://www.youtube.com',\r\n      'Referer': 'https://www.youtube.com/',\r\n      'X-YouTube-Client-Name': '1',\r\n      'X-YouTube-Client-Version': '2.20231219.04.00',\r\n    };\r\n  }\r\n\r\n  private generateRandomIP(): string {\r\n    // Generate a realistic residential IP range (avoid datacenter IPs)\r\n    const ranges = [\r\n      [73, 75],   // Comcast\r\n      [96, 97],   // Time Warner\r\n      [108, 109], // AT&T\r\n      [24, 24],   // Residential ranges\r\n    ];\r\n    \r\n    const range = ranges[Math.floor(Math.random() * ranges.length)];\r\n    return `${range[0]}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`;\r\n  }\r\n\r\n  private getForwardedHeaders(): Record<string, string> {\r\n    if (!this.isCloudFunction) {\r\n      return {}; // No special headers for local development\r\n    }\r\n\r\n    if (this.proxyConfig) {\r\n      // Use actual proxy IP - more realistic than fake IPs\r\n      return {\r\n        'X-Forwarded-For': this.proxyConfig.host,\r\n        'X-Real-IP': this.proxyConfig.host,\r\n        'Via': `1.1 ${this.proxyConfig.host}`,\r\n      };\r\n    } else {\r\n      // Fallback to fake residential IPs when no proxy\r\n      const fakeIP = this.generateRandomIP();\r\n      return {\r\n        'X-Forwarded-For': fakeIP,\r\n        'X-Real-IP': fakeIP,\r\n      };\r\n    }\r\n  }\r\n\r\n  private parseCookieString(cookieString: string): Cookie[] {\r\n    return cookieString.split(';').map(cookie => {\r\n      const [name, value] = cookie.trim().split('=');\r\n      return {\r\n        name: name?.trim() || '',\r\n        value: value?.trim() || '',\r\n        domain: '.youtube.com',\r\n        path: '/',\r\n        secure: true,\r\n        httpOnly: false,\r\n        sameSite: 'no_restriction' as const\r\n      } as Cookie;\r\n    }).filter(cookie => cookie.name && cookie.value);\r\n  }\r\n\r\n  private formatCookies(cookies: Cookie[]): string {\r\n    return cookies.map(cookie => `${cookie.name}=${cookie.value}`).join('; ');\r\n  }\r\n\r\n  private extractVisitorData(cookies: Cookie[]): string {\r\n    // Try to extract visitor data from various cookie sources\r\n    const visitorCookies = [\r\n      'VISITOR_INFO1_LIVE',\r\n      'VISITOR_PRIVACY_METADATA'\r\n    ];\r\n\r\n    for (const cookieName of visitorCookies) {\r\n      const cookie = cookies.find(c => c.name === cookieName);\r\n      if (cookie?.value) {\r\n        return cookie.value;\r\n      }\r\n    }\r\n\r\n    // Generate a basic visitor data if none found\r\n    console.warn('⚠️  No visitor data found in cookies, using fallback');\r\n    return this.generateFallbackVisitorData();\r\n  }\r\n\r\n  private generateFallbackVisitorData(): string {\r\n    // Generate a basic visitor data structure\r\n    const timestamp = Date.now();\r\n    const random = Math.random().toString(36).substring(2, 15);\r\n    return btoa(`visitor_${timestamp}_${random}`).replace(/[^A-Za-z0-9]/g, '');\r\n  }\r\n\r\n  private extractVideoId(url: string): string {\r\n    const patterns = [\r\n      /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/)([^&\\n?#]+)/,\r\n      /^([a-zA-Z0-9_-]{11})$/\r\n    ];\r\n\r\n    for (const pattern of patterns) {\r\n      const match = url.match(pattern);\r\n      if (match) return match[1];\r\n    }\r\n\r\n    throw new YtdlMp3Error(`Invalid YouTube URL: ${url}`);\r\n  }\r\n\r\n  async getVideoInfo(url: string): Promise<VideoInfo> {\r\n    const videoId = this.extractVideoId(url);\r\n    const maxRetries = this.isCloudFunction ? 7 : 3; // Even more retries for cloud functions\r\n    \r\n    // AGGRESSIVE ANTI-BOT: Warm up the session first\r\n    if (this.isCloudFunction) {\r\n      await this.warmUpSession();\r\n    }\r\n    \r\n    for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n      try {\r\n        // Progressive delays that mimic human behavior\r\n        if (attempt > 1) {\r\n          const delay = this.calculateHumanDelay(attempt);\r\n          console.log(`⏳ Human-like delay: ${delay}ms before attempt ${attempt}...`);\r\n          await this.sleep(delay);\r\n          \r\n          // ANTI-BOT: Rotate headers and user agent between attempts\r\n          this.rotateSessionFingerprint();\r\n        }\r\n\r\n        // Try the primary API endpoint\r\n        return await this.fetchFromPlayerApi(videoId);\r\n      } catch (error) {\r\n        const errorMessage = error instanceof Error ? error.message : String(error);\r\n        console.warn(`⚠️  Attempt ${attempt} failed: ${errorMessage}`);\r\n\r\n        // Check if it's a bot detection error\r\n        if (this.isBotDetectionError(errorMessage)) {\r\n          if (attempt < maxRetries) {\r\n            console.log(`🤖 Bot detection triggered, escalating stealth measures...`);\r\n            \r\n            // ESCALATION: Try multiple stealth approaches\r\n            const stealthMethods = [\r\n              () => this.fetchFromWebPageWithRotation(videoId),\r\n              () => this.fetchFromEmbedPage(videoId),\r\n              () => this.fetchWithProxyHeaders(videoId),\r\n              () => this.fetchFromMobilePage(videoId),\r\n            ];\r\n            \r\n            for (const method of stealthMethods) {\r\n              try {\r\n                const result = await method();\r\n                console.log(`✅ Stealth method succeeded!`);\r\n                return result;\r\n              } catch (stealthError) {\r\n                console.warn(`⚠️  Stealth method failed: ${stealthError}`);\r\n                await this.sleep(1000); // Small delay between stealth attempts\r\n              }\r\n            }\r\n            \r\n            console.warn(`❌ All stealth methods failed, continuing with next attempt...`);\r\n            continue;\r\n          }\r\n        }\r\n\r\n        // If it's the last attempt, try all fallback methods\r\n        if (attempt === maxRetries) {\r\n          console.warn('🔄 Final desperate attempt with all methods...');\r\n          try {\r\n            return await this.fetchFromWebPage(videoId);\r\n          } catch (fallbackError) {\r\n            throw new YtdlMp3Error(`Failed to fetch video info after ${maxRetries} attempts: ${errorMessage}`);\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    throw new YtdlMp3Error(`Failed to fetch video info after ${maxRetries} attempts`);\r\n  }\r\n\r\n  private calculateHumanDelay(attempt: number): number {\r\n    // Mimic human behavior: start fast, then slow down more when frustrated\r\n    const baseDelay = this.isCloudFunction ? 3000 : 1000;\r\n    const escalation = Math.pow(attempt - 1, 1.5) * 1000;\r\n    const randomness = Math.random() * 2000; // Add human-like randomness\r\n    \r\n    return Math.min(baseDelay + escalation + randomness, 15000); // Cap at 15 seconds\r\n  }\r\n\r\n  private rotateSessionFingerprint(): void {\r\n    // Change user agent to a different one\r\n    this.userAgent = this.getOptimalUserAgent();\r\n    \r\n    // Update headers with new fingerprint\r\n    const newHeaders = this.buildOptimalHeaders();\r\n    Object.assign(this.httpClient.defaults.headers, newHeaders);\r\n    \r\n    console.log(`🔄 Rotated to new browser fingerprint`);\r\n  }\r\n\r\n  private async warmUpSession(): Promise<void> {\r\n    try {\r\n      console.log(`🔥 Warming up session to appear more human...`);\r\n      \r\n      // Make a gentle request to YouTube's homepage first\r\n      await this.httpClient.get('https://www.youtube.com', {\r\n        timeout: 10000,\r\n        validateStatus: () => true // Accept any status\r\n      });\r\n      \r\n      await this.sleep(1000 + Math.random() * 2000); // Random delay\r\n      \r\n      // Make a search request to look more human\r\n      const searchTerms = ['music', 'video', 'entertainment', 'news', 'comedy'];\r\n      const randomTerm = searchTerms[Math.floor(Math.random() * searchTerms.length)];\r\n      \r\n      await this.httpClient.get(`https://www.youtube.com/results?search_query=${randomTerm}`, {\r\n        timeout: 10000,\r\n        validateStatus: () => true\r\n      });\r\n      \r\n      await this.sleep(500 + Math.random() * 1000);\r\n      \r\n      console.log(`✅ Session warmed up`);\r\n    } catch (error) {\r\n      console.warn(`⚠️  Session warmup failed (continuing anyway): ${error}`);\r\n    }\r\n  }\r\n\r\n  private isBotDetectionError(errorMessage: string): boolean {\r\n    const botIndicators = [\r\n      'Sign in to confirm you\\'re not a bot',\r\n      'not a bot',\r\n      'bot detection',\r\n      'automated',\r\n      'suspicious activity',\r\n      'verify you\\'re human',\r\n      'captcha',\r\n      'rate limit'\r\n    ];\r\n\r\n    return botIndicators.some(indicator => \r\n      errorMessage.toLowerCase().includes(indicator.toLowerCase())\r\n    );\r\n  }\r\n\r\n  private async sleep(ms: number): Promise<void> {\r\n    return new Promise(resolve => setTimeout(resolve, ms));\r\n  }\r\n\r\n  private async fetchFromPlayerApi(videoId: string): Promise<VideoInfo> {\r\n    const url = 'https://www.youtube.com/youtubei/v1/player';\r\n    \r\n    const payload = {\r\n      context: {\r\n        client: {\r\n          clientName: 'WEB',\r\n          clientVersion: '2.20231219.04.00',\r\n          visitorData: this.visitorData,\r\n        },\r\n        user: {\r\n          lockedSafetyMode: false\r\n        }\r\n      },\r\n      videoId: videoId\r\n    };\r\n\r\n    const response = await this.httpClient.post(url, payload);\r\n    return await this.parsePlayerResponse(response.data);\r\n  }\r\n\r\n  private async fetchFromWebPage(videoId: string): Promise<VideoInfo> {\r\n    // Fallback method: extract from YouTube webpage\r\n    const watchUrl = `https://www.youtube.com/watch?v=${videoId}`;\r\n    \r\n    const response = await this.httpClient.get(watchUrl);\r\n    const html = response.data;\r\n\r\n    // Extract JSON data from the page\r\n    const playerDataMatch = html.match(/var ytInitialPlayerResponse = ({.+?});/);\r\n    if (!playerDataMatch) {\r\n      throw new YtdlMp3Error('Could not extract player data from webpage');\r\n    }\r\n\r\n    const playerData = JSON.parse(playerDataMatch[1]);\r\n    return await this.parsePlayerResponse(playerData);\r\n  }\r\n\r\n  private async fetchFromWebPageWithRotation(videoId: string): Promise<VideoInfo> {\r\n    // Enhanced fallback method with header rotation for bot detection\r\n    const watchUrl = `https://www.youtube.com/watch?v=${videoId}`;\r\n    \r\n    // Create a temporary axios instance with modified headers\r\n    const rotatedHeaders = {\r\n      ...this.buildOptimalHeaders(),\r\n      'User-Agent': this.getRandomMobileUserAgent(),\r\n      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n      'Accept-Language': 'en-US,en;q=0.5',\r\n      'Cache-Control': 'max-age=0',\r\n      'Sec-Fetch-Mode': 'navigate',\r\n      'Sec-Fetch-Site': 'same-origin',\r\n      'Sec-Fetch-User': '?1',\r\n      'Referer': 'https://www.youtube.com/',\r\n    };\r\n\r\n    const tempClient = axios.create({\r\n      timeout: this.httpClient.defaults.timeout,\r\n      headers: rotatedHeaders,\r\n    });\r\n\r\n    const response = await tempClient.get(watchUrl);\r\n    const html = response.data;\r\n\r\n    // Extract JSON data from the page\r\n    const playerDataMatch = html.match(/var ytInitialPlayerResponse = ({.+?});/);\r\n    if (!playerDataMatch) {\r\n      throw new YtdlMp3Error('Could not extract player data from webpage');\r\n    }\r\n\r\n    const playerData = JSON.parse(playerDataMatch[1]);\r\n    return await this.parsePlayerResponse(playerData);\r\n  }\r\n\r\n  private getRandomMobileUserAgent(): string {\r\n    const mobileUserAgents = [\r\n      'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1',\r\n      'Mozilla/5.0 (iPad; CPU OS 17_2_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1',\r\n      'Mozilla/5.0 (Linux; Android 14; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36',\r\n      'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36',\r\n    ];\r\n\r\n    return mobileUserAgents[Math.floor(Math.random() * mobileUserAgents.length)];\r\n  }\r\n\r\n  private async fetchFromEmbedPage(videoId: string): Promise<VideoInfo> {\r\n    // Try to extract from YouTube embed page (often has different bot detection)\r\n    const embedUrl = `https://www.youtube.com/embed/${videoId}`;\r\n    \r\n    const tempClient = axios.create({\r\n      timeout: this.httpClient.defaults.timeout,\r\n      headers: {\r\n        'User-Agent': this.getRandomMobileUserAgent(),\r\n        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n        'Accept-Language': 'en-US,en;q=0.5',\r\n        'Accept-Encoding': 'gzip, deflate, br',\r\n        'Referer': 'https://www.youtube.com/',\r\n        'Sec-Fetch-Dest': 'iframe',\r\n        'Sec-Fetch-Mode': 'navigate',\r\n        'Sec-Fetch-Site': 'same-origin',\r\n        'Cookie': this.cookies,\r\n      },\r\n    });\r\n\r\n    const response = await tempClient.get(embedUrl);\r\n    const html = response.data;\r\n\r\n    // Extract JSON data from embed page\r\n    const playerDataMatch = html.match(/var ytInitialPlayerResponse = ({.+?});/) || \r\n                           html.match(/\"PLAYER_CONFIG\":({.+?}),\"EXPERIMENT_FLAGS\"/) ||\r\n                           html.match(/ytplayer\\.config\\s*=\\s*({.+?});/);\r\n    \r\n    if (!playerDataMatch) {\r\n      throw new YtdlMp3Error('Could not extract player data from embed page');\r\n    }\r\n\r\n    const playerData = JSON.parse(playerDataMatch[1]);\r\n    return await this.parsePlayerResponse(playerData);\r\n  }\r\n\r\n  private async fetchWithProxyHeaders(videoId: string): Promise<VideoInfo> {\r\n    // Use headers that mimic residential proxy traffic\r\n    const forwardedHeaders = this.getForwardedHeaders();\r\n    const proxyHeaders = {\r\n      'User-Agent': this.getOptimalUserAgent(),\r\n      'Accept': 'application/json, text/plain, */*',\r\n      'Accept-Language': 'en-US,en;q=0.9',\r\n      'Accept-Encoding': 'gzip, deflate, br',\r\n      'Cache-Control': 'no-cache',\r\n      'Pragma': 'no-cache',\r\n      ...forwardedHeaders,\r\n      'X-Forwarded-Proto': 'https',\r\n      'Cookie': this.cookies,\r\n      'Referer': 'https://www.google.com/',\r\n      'Origin': 'https://www.youtube.com',\r\n    };\r\n\r\n    const tempClient = axios.create({\r\n      timeout: this.httpClient.defaults.timeout,\r\n      headers: proxyHeaders,\r\n    });\r\n\r\n    const url = 'https://www.youtube.com/youtubei/v1/player';\r\n    \r\n    const payload = {\r\n      context: {\r\n        client: {\r\n          clientName: 'WEB',\r\n          clientVersion: '2.20231219.04.00',\r\n          visitorData: this.visitorData,\r\n        },\r\n        user: {\r\n          lockedSafetyMode: false\r\n        }\r\n      },\r\n      videoId: videoId\r\n    };\r\n\r\n    const response = await tempClient.post(url, payload);\r\n    return await this.parsePlayerResponse(response.data);\r\n  }\r\n\r\n  private async fetchFromMobilePage(videoId: string): Promise<VideoInfo> {\r\n    // Use mobile YouTube interface (different bot detection rules)\r\n    const mobileWatchUrl = `https://m.youtube.com/watch?v=${videoId}`;\r\n    \r\n    const tempClient = axios.create({\r\n      timeout: this.httpClient.defaults.timeout,\r\n      headers: {\r\n        'User-Agent': this.getRandomMobileUserAgent(),\r\n        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\r\n        'Accept-Language': 'en-US,en;q=0.5',\r\n        'Accept-Encoding': 'gzip, deflate, br',\r\n        'Viewport-Width': '375',\r\n        'Sec-CH-UA-Mobile': '?1',\r\n        'Sec-CH-UA-Platform': '\"iOS\"',\r\n        'Sec-Fetch-Dest': 'document',\r\n        'Sec-Fetch-Mode': 'navigate',\r\n        'Sec-Fetch-Site': 'none',\r\n        'Sec-Fetch-User': '?1',\r\n        'Cookie': this.cookies,\r\n        'Referer': 'https://m.youtube.com/',\r\n      },\r\n    });\r\n\r\n    const response = await tempClient.get(mobileWatchUrl);\r\n    const html = response.data;\r\n\r\n    // Mobile page has different JSON structure\r\n    const playerDataMatch = html.match(/var ytInitialPlayerResponse = ({.+?});/) ||\r\n                           html.match(/window\\[\"ytInitialPlayerResponse\"\\] = ({.+?});/) ||\r\n                           html.match(/\"playerResponse\":\"({.+?})\"/);\r\n    \r\n    if (!playerDataMatch) {\r\n      throw new YtdlMp3Error('Could not extract player data from mobile page');\r\n    }\r\n\r\n    let playerData;\r\n    if (playerDataMatch[0].includes('\"playerResponse\"')) {\r\n      // Handle escaped JSON in mobile\r\n      playerData = JSON.parse(JSON.parse(playerDataMatch[1]));\r\n    } else {\r\n      playerData = JSON.parse(playerDataMatch[1]);\r\n    }\r\n\r\n    return await this.parsePlayerResponse(playerData);\r\n  }\r\n\r\n  private async parsePlayerResponse(data: any): Promise<VideoInfo> {\r\n    const playerResponse = data.playerResponse || data;\r\n    \r\n    if (playerResponse.playabilityStatus?.status !== 'OK') {\r\n      throw new YtdlMp3Error(`Video unavailable: ${playerResponse.playabilityStatus?.reason || 'Unknown error'}`);\r\n    }\r\n\r\n    const videoDetails = playerResponse.videoDetails;\r\n    const streamingData = playerResponse.streamingData;\r\n\r\n    if (!streamingData?.formats && !streamingData?.adaptiveFormats) {\r\n      throw new YtdlMp3Error('No streaming data found');\r\n    }\r\n\r\n    const allFormats = [\r\n      ...(streamingData.formats || []),\r\n      ...(streamingData.adaptiveFormats || [])\r\n    ];\r\n\r\n    // Parse formats asynchronously with videoId\r\n    const formatResults = await Promise.all(\r\n      allFormats.map(format => this.parseFormat(format, videoDetails.videoId))\r\n    );\r\n\r\n    // Filter out null results and keep only formats with audio\r\n    const formats = formatResults\r\n      .filter((f): f is VideoFormat => f !== null)\r\n      .filter(f => f.hasAudio); // Only audio formats for MP3 conversion\r\n\r\n    if (formats.length === 0) {\r\n      throw new YtdlMp3Error('No usable audio formats found after processing');\r\n    }\r\n\r\n    return {\r\n      videoId: videoDetails.videoId,\r\n      title: videoDetails.title,\r\n      lengthSeconds: videoDetails.lengthSeconds,\r\n      author: videoDetails.author,\r\n      description: videoDetails.shortDescription || '',\r\n      formats: formats\r\n    };\r\n  }\r\n\r\n  private async parseFormat(format: any, videoId: string): Promise<VideoFormat | null> {\r\n    try {\r\n      const url = format.url || await this.buildUrl(format, videoId);\r\n      \r\n      return {\r\n        itag: format.itag,\r\n        url: url,\r\n        mimeType: format.mimeType || '',\r\n        bitrate: format.bitrate || 0,\r\n        audioQuality: format.audioQuality || '',\r\n        audioSampleRate: format.audioSampleRate || '',\r\n        audioChannels: format.audioChannels || 0,\r\n        contentLength: format.contentLength || '0',\r\n        quality: format.quality || format.qualityLabel || '',\r\n        hasAudio: !!(format.audioQuality || format.audioSampleRate),\r\n        hasVideo: !!(format.width && format.height)\r\n      };\r\n    } catch (error) {\r\n      console.warn(`⚠️  Skipping format ${format.itag}: ${(error as Error).message}`);\r\n      return null; // Skip formats that can't be processed\r\n    }\r\n  }\r\n\r\n  private async buildUrl(format: any, videoId: string): Promise<string> {\r\n    console.log('🔗 Building URL for format:', { \r\n      itag: format.itag, \r\n      hasUrl: !!format.url, \r\n      hasSignatureCipher: !!format.signatureCipher,\r\n      hasCipher: !!format.cipher,\r\n      hasInitRange: !!format.initRange,\r\n      mimeType: format.mimeType\r\n    });\r\n    \r\n    if (format.url) {\r\n      console.log('✅ Direct URL available');\r\n      return format.url;\r\n    }\r\n    \r\n    // Handle encrypted signatures\r\n    const cipher = format.signatureCipher || format.cipher;\r\n    if (cipher) {\r\n      console.log('🔐 Found cipher, attempting decryption...');\r\n      try {\r\n        const decryptedUrl = await SignatureDecryptor.decryptSignature(cipher, videoId);\r\n        console.log('✅ Decryption completed, URL length:', decryptedUrl.length);\r\n        return decryptedUrl;\r\n      } catch (error) {\r\n        console.warn('⚠️  Signature decryption failed:', (error as Error).message);\r\n        \r\n        // PRACTICAL SOLUTION: Try direct cipher URL extraction\r\n        try {\r\n          const directUrl = this.extractDirectUrlFromCipher(cipher);\r\n          if (directUrl) {\r\n            console.log('🔧 Extracted direct URL from cipher');\r\n            return directUrl;\r\n          }\r\n        } catch (directError) {\r\n          console.warn('⚠️  Direct URL extraction failed:', (directError as Error).message);\r\n        }\r\n      }\r\n    }\r\n    \r\n    // For DASH formats with no direct URL, try to get streaming URL from authenticated API\r\n    if (format.initRange && format.indexRange) {\r\n      console.log('🎯 DASH format detected, trying authenticated streaming URL...');\r\n      try {\r\n        const streamingUrl = await this.getAuthenticatedStreamingUrl(videoId, format.itag);\r\n        if (streamingUrl) {\r\n          console.log('✅ Got authenticated streaming URL');\r\n          return streamingUrl;\r\n        }\r\n      } catch (error) {\r\n        console.warn('⚠️  Authenticated streaming URL failed:', (error as Error).message);\r\n      }\r\n    }\r\n    \r\n    console.error('❌ No valid URL found for format:', JSON.stringify(format, null, 2));\r\n    throw new YtdlMp3Error('No valid URL found for format');\r\n  }\r\n\r\n  /**\r\n   * Extract direct URL from cipher without full decryption (practical fallback)\r\n   */\r\n  private extractDirectUrlFromCipher(cipher: string): string | null {\r\n    try {\r\n      // Parse cipher parameters\r\n      const params = new URLSearchParams(cipher);\r\n      const baseUrl = params.get('url');\r\n      \r\n      if (!baseUrl) {\r\n        return null;\r\n      }\r\n      \r\n      // Decode the base URL\r\n      const decodedUrl = decodeURIComponent(baseUrl);\r\n      \r\n      // Check if it's a valid YouTube streaming URL\r\n      if (decodedUrl.includes('googlevideo.com') && decodedUrl.includes('videoplayback')) {\r\n        console.log('🎯 Found direct streaming URL in cipher');\r\n        \r\n        // Parse the URL to add additional authentication parameters if needed\r\n        const url = new URL(decodedUrl);\r\n        \r\n        // Add required authentication parameters that might be missing\r\n        if (!url.searchParams.has('c')) {\r\n          url.searchParams.set('c', 'WEB');\r\n        }\r\n        if (!url.searchParams.has('cver')) {\r\n          url.searchParams.set('cver', '2.20231219.04.00');\r\n        }\r\n        if (!url.searchParams.has('cplayer')) {\r\n          url.searchParams.set('cplayer', 'UNIPLAYER');\r\n        }\r\n        if (!url.searchParams.has('cbr')) {\r\n          url.searchParams.set('cbr', 'Chrome');\r\n        }\r\n        if (!url.searchParams.has('cbrver')) {\r\n          url.searchParams.set('cbrver', '120.0.0.0');\r\n        }\r\n        \r\n        // Add visitor data for authentication\r\n        if (this.visitorData && !url.searchParams.has('visitor_data')) {\r\n          url.searchParams.set('visitor_data', this.visitorData);\r\n        }\r\n        \r\n        const enhancedUrl = url.toString();\r\n        console.log(`🔧 Enhanced URL with auth parameters (length: ${enhancedUrl.length})`);\r\n        return enhancedUrl;\r\n      }\r\n      \r\n      return null;\r\n    } catch (error) {\r\n      console.warn('⚠️  Error in cipher URL extraction:', (error as Error).message);\r\n      return null;\r\n    }\r\n  }\r\n\r\n        async downloadAudioStream(videoUrl: string): Promise<Buffer> {\r\n    console.log(`📥 Using ytdl.downloadFromInfo with pre-fetched video info...`);\r\n    console.log(`☁️  Cloud function mode: ${this.isCloudFunction}`);\r\n    \r\n    const maxRetries = this.isCloudFunction ? 3 : 2;\r\n    \r\n    for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n      try {\r\n        if (attempt > 1) {\r\n          const delay = this.requestDelay * attempt;\r\n          console.log(`⏳ Waiting ${delay}ms before download attempt ${attempt}...`);\r\n          await this.sleep(delay);\r\n        }\r\n\r\n        // Use our already-fetched video info (with anti-bot measures)\r\n        console.log(`📡 Using pre-fetched video info (bypassing ytdl-core's requests)...`);\r\n        const videoInfo = await this.getVideoInfo(videoUrl);\r\n        \r\n        // Convert our VideoInfo to ytdl-core format\r\n        const ytdlInfo = this.convertToYtdlFormat(videoInfo, videoUrl);\r\n        \r\n        // Create authenticated agent with our cookies and proxy\r\n        const cookieArray = this.parseCookiesForYtdl();\r\n        \r\n        // Enhanced agent options with proxy support\r\n        const agentOptions: any = {};\r\n        if (this.proxyConfig) {\r\n          agentOptions.proxy = {\r\n            host: this.proxyConfig.host,\r\n            port: this.proxyConfig.port,\r\n            auth: this.proxyConfig.username ? {\r\n              username: this.proxyConfig.username,\r\n              password: this.proxyConfig.password || ''\r\n            } : undefined\r\n          };\r\n        }\r\n        \r\n        const agent = ytdl.createAgent(cookieArray, agentOptions);\r\n        \r\n        console.log(`🍪 Created ytdl agent with ${cookieArray.length} cookies (attempt ${attempt})`);\r\n        if (this.proxyConfig) {\r\n          console.log(`🌐 Using proxy for download: ${this.proxyConfig.host}:${this.proxyConfig.port}`);\r\n        }\r\n        \r\n        // Enhanced ytdl options for cloud functions\r\n        const ytdlOptions = {\r\n          agent,\r\n          quality: 'highestaudio' as const,\r\n          filter: 'audioonly' as const,\r\n          requestOptions: {\r\n            headers: {\r\n              'User-Agent': this.isCloudFunction ? this.getRandomMobileUserAgent() : this.userAgent,\r\n              'Accept': '*/*',\r\n              'Accept-Language': 'en-US,en;q=0.9',\r\n              'Accept-Encoding': 'gzip, deflate, br',\r\n              'Cache-Control': 'no-cache',\r\n              'DNT': '1',\r\n              'Connection': 'keep-alive',\r\n              ...(this.isCloudFunction && this.getForwardedHeaders())\r\n            },\r\n            timeout: this.isCloudFunction ? 60000 : 30000,\r\n          }\r\n        };\r\n        \r\n        // Use downloadFromInfo with our pre-fetched video info\r\n        console.log(`🎵 Starting download with ytdl.downloadFromInfo (attempt ${attempt})...`);\r\n        console.log(`📋 Video: ${videoInfo.title} by ${videoInfo.author}`);\r\n        const stream = ytdl.downloadFromInfo(ytdlInfo, ytdlOptions);\r\n        \r\n        \r\n        // Convert stream to buffer\r\n        const chunks: Buffer[] = [];\r\n        let totalSize = 0;\r\n        \r\n        return new Promise((resolve, reject) => {\r\n          stream.on('data', (chunk: Buffer) => {\r\n            chunks.push(chunk);\r\n            totalSize += chunk.length;\r\n            if (chunks.length % 100 === 0) {\r\n              console.log(`📦 Downloaded ${(totalSize / 1024 / 1024).toFixed(2)}MB...`);\r\n            }\r\n          });\r\n          \r\n          stream.on('end', () => {\r\n            const buffer = Buffer.concat(chunks as any);\r\n            console.log(`✅ Download complete! ${buffer.length} bytes (${(buffer.length / 1024 / 1024).toFixed(2)}MB)`);\r\n            \r\n            if (buffer.length === 0) {\r\n              reject(new YtdlMp3Error('Downloaded empty buffer'));\r\n              return;\r\n            }\r\n            \r\n            resolve(buffer);\r\n          });\r\n          \r\n          stream.on('error', (error: any) => {\r\n            console.error(`❌ ytdl-core stream error (attempt ${attempt}):`, error.message);\r\n            \r\n            // Check for bot detection errors\r\n            if (this.isBotDetectionError(error.message)) {\r\n              reject(new YtdlMp3Error(`Bot detection during stream download: ${error.message}`));\r\n              return;\r\n            }\r\n            \r\n            if (error.message.includes('403')) {\r\n              reject(new YtdlMp3Error('Stream access denied. Try refreshing cookies.'));\r\n            } else if (error.message.includes('404')) {\r\n              reject(new YtdlMp3Error('Stream not found. Video may be unavailable.'));\r\n            } else {\r\n              reject(new YtdlMp3Error(`Stream download failed: ${error.message}`));\r\n            }\r\n          });\r\n          \r\n          // Extended timeout for cloud functions\r\n          const timeout = this.isCloudFunction ? 180000 : 120000; // 3 minutes vs 2 minutes\r\n          setTimeout(() => {\r\n            stream.destroy();\r\n            reject(new YtdlMp3Error(`Download timeout after ${timeout / 1000} seconds`));\r\n          }, timeout);\r\n        });\r\n        \r\n      } catch (error: any) {\r\n        const errorMessage = error instanceof Error ? error.message : String(error);\r\n        console.error(`❌ ytdl-core download attempt ${attempt} failed:`, errorMessage);\r\n        \r\n        // Check if it's a bot detection error\r\n        if (this.isBotDetectionError(errorMessage)) {\r\n          if (attempt < maxRetries) {\r\n            console.log(`🤖 Bot detection during download, retrying with different approach...`);\r\n            continue;\r\n          }\r\n        }\r\n        \r\n        // If it's the last attempt, throw the error\r\n        if (attempt === maxRetries) {\r\n          throw new YtdlMp3Error(`Stream download failed after ${maxRetries} attempts: ${errorMessage}`);\r\n        }\r\n      }\r\n    }\r\n    \r\n    throw new YtdlMp3Error(`Stream download failed after ${maxRetries} attempts`);\r\n  }\r\n\r\n  /**\r\n   * Convert our cookie string to ytdl-core cookie array format\r\n   */\r\n  private parseCookiesForYtdl(): Cookie[] {\r\n    try {\r\n      // If cookies is already an array (from cookies.json), return it\r\n      if (Array.isArray(this.cookies)) {\r\n        return this.cookies as Cookie[];\r\n      }\r\n      \r\n      // Convert cookie string to ytdl-core format\r\n      // Note: This is a simplified conversion. For full functionality,\r\n      // use the cookies.json format directly\r\n      return this.cookies.split(';').map(cookie => {\r\n        const [name, value] = cookie.trim().split('=');\r\n        return {\r\n          name: name?.trim() || '',\r\n          value: value?.trim() || '',\r\n          domain: '.youtube.com',\r\n          path: '/',\r\n          secure: true,\r\n          httpOnly: false,\r\n          sameSite: 'no_restriction' as const\r\n        } as Cookie;\r\n      }).filter(cookie => cookie.name && cookie.value);\r\n      \r\n    } catch (error) {\r\n      console.warn('⚠️  Cookie parsing failed, using empty array:', (error as Error).message);\r\n      return [];\r\n    }\r\n  }\r\n\r\n  // Get the best audio format for MP3 conversion\r\n  getBestAudioFormat(formats: VideoFormat[]): VideoFormat {\r\n    const audioFormats = formats.filter(f => f.hasAudio);\r\n    \r\n    console.log(`🔍 Available formats: ${audioFormats.map(f => `itag ${f.itag} (${f.mimeType})`).join(', ')}`);\r\n    \r\n    // PRACTICAL APPROACH: Prefer formats with direct URLs (no encryption)\r\n    const directUrlFormats = audioFormats.filter(f => f.url && !f.url.includes('signature'));\r\n    if (directUrlFormats.length > 0) {\r\n      console.log('✅ Found formats with direct URLs');\r\n      const bestDirect = directUrlFormats.sort((a, b) => (b.bitrate || 0) - (a.bitrate || 0))[0];\r\n      console.log(`🎯 Selected direct URL format: itag ${bestDirect.itag}`);\r\n      return bestDirect;\r\n    }\r\n    \r\n    // Prefer audio-only formats (typically easier to decrypt)\r\n    const audioOnlyFormats = audioFormats.filter(f => f.hasAudio && !f.hasVideo);\r\n    \r\n    // Prefer high-quality audio formats that are commonly working\r\n    const preferredItags = [141, 140, 139, 251, 250, 249, 18]; // Include legacy format 18\r\n    \r\n    for (const itag of preferredItags) {\r\n      const format = audioOnlyFormats.find(f => f.itag === itag) || audioFormats.find(f => f.itag === itag);\r\n      if (format) {\r\n        console.log(`🎯 Selected preferred format: itag ${format.itag}`);\r\n        return format;\r\n      }\r\n    }\r\n\r\n    // Fallback to highest bitrate audio format\r\n    const fallback = audioFormats.sort((a, b) => (b.bitrate || 0) - (a.bitrate || 0))[0];\r\n    console.log(`🔄 Fallback to highest bitrate: itag ${fallback.itag}`);\r\n    return fallback;\r\n  }\r\n\r\n  // Health check method\r\n  isAuthenticated(): boolean {\r\n    return this.cookies.includes('SID') || this.cookies.includes('LOGIN_INFO');\r\n  }\r\n\r\n  getCookieCount(): number {\r\n    return this.cookies.split(';').length;\r\n  }\r\n\r\n  /**\r\n   * Get authenticated streaming URL for DASH formats\r\n   */\r\n  private async getAuthenticatedStreamingUrl(videoId: string, itag: number): Promise<string | null> {\r\n    try {\r\n      // Use YouTube's get_video_info endpoint with authentication\r\n      const params = new URLSearchParams({\r\n        video_id: videoId,\r\n        el: 'detailpage',\r\n        ps: 'default',\r\n        eurl: '',\r\n        gl: 'US',\r\n        hl: 'en',\r\n        has_verified: '1',\r\n        bpctr: Math.floor(Date.now() / 1000).toString(),\r\n        fbv: '0',\r\n        cbr: 'Chrome',\r\n        cbrver: '120.0.0.0',\r\n        c: 'WEB',\r\n        cver: '2.20231219.04.00',\r\n        cplayer: 'UNIPLAYER',\r\n        cosver: '10.0',\r\n        cplatform: 'DESKTOP'\r\n      });\r\n\r\n      const response = await this.httpClient.get(\r\n        `https://www.youtube.com/get_video_info?${params.toString()}`\r\n      );\r\n\r\n      // Parse the response\r\n      const videoInfo = new URLSearchParams(response.data);\r\n      const playerResponse = videoInfo.get('player_response');\r\n      \r\n      if (!playerResponse) {\r\n        return null;\r\n      }\r\n\r\n      const data = JSON.parse(playerResponse);\r\n      const streamingData = data.streamingData;\r\n      \r\n      if (!streamingData) {\r\n        return null;\r\n      }\r\n\r\n      // Look for the specific format\r\n      const allFormats = [\r\n        ...(streamingData.formats || []),\r\n        ...(streamingData.adaptiveFormats || [])\r\n      ];\r\n\r\n      const targetFormat = allFormats.find(f => f.itag === itag);\r\n      \r\n      if (targetFormat && targetFormat.url) {\r\n        return targetFormat.url;\r\n      }\r\n\r\n      return null;\r\n    } catch (error) {\r\n      console.warn('Failed to get authenticated streaming URL:', (error as Error).message);\r\n      return null;\r\n    }\r\n  }\r\n\r\n  private convertToYtdlFormat(videoInfo: VideoInfo, videoUrl: string): any {\r\n    // Convert our VideoInfo format to ytdl-core's expected format\r\n    console.log(`🔄 Converting video info to ytdl-core format...`);\r\n    \r\n    // Get the best audio format from our processed formats\r\n    const bestFormat = this.getBestAudioFormat(videoInfo.formats);\r\n    \r\n    // Convert our formats to ytdl-core format\r\n    const ytdlFormats = videoInfo.formats.map(format => ({\r\n      itag: format.itag,\r\n      url: format.url,\r\n      mimeType: format.mimeType,\r\n      bitrate: format.bitrate,\r\n      audioQuality: format.audioQuality,\r\n      audioSampleRate: format.audioSampleRate,\r\n      audioChannels: format.audioChannels,\r\n      contentLength: format.contentLength,\r\n      quality: format.quality,\r\n      hasAudio: format.hasAudio,\r\n      hasVideo: format.hasVideo,\r\n      // Additional ytdl-core specific properties\r\n      container: format.mimeType?.split('/')[1]?.split(';')[0] || 'unknown',\r\n      encoding: format.mimeType?.includes('opus') ? 'opus' : \r\n               format.mimeType?.includes('mp4') ? 'aac' : 'unknown'\r\n    }));\r\n\r\n    // Create ytdl-core compatible info object\r\n    const ytdlInfo = {\r\n      videoDetails: {\r\n        videoId: videoInfo.videoId,\r\n        title: videoInfo.title,\r\n        lengthSeconds: videoInfo.lengthSeconds,\r\n        author: {\r\n          name: videoInfo.author\r\n        },\r\n        description: videoInfo.description,\r\n        viewCount: '0', // We don't fetch this\r\n        uploadDate: new Date().toISOString().split('T')[0].replace(/-/g, ''),\r\n        isLiveContent: false\r\n      },\r\n      formats: ytdlFormats,\r\n      // Include the original URL for ytdl-core\r\n      player_response: {\r\n        videoDetails: {\r\n          videoId: videoInfo.videoId,\r\n          title: videoInfo.title,\r\n          lengthSeconds: videoInfo.lengthSeconds,\r\n          author: videoInfo.author,\r\n          shortDescription: videoInfo.description\r\n        },\r\n        streamingData: {\r\n          formats: ytdlFormats.filter(f => f.hasVideo && f.hasAudio),\r\n          adaptiveFormats: ytdlFormats.filter(f => f.hasAudio && !f.hasVideo)\r\n        }\r\n      },\r\n      // Additional metadata that ytdl-core might expect\r\n      full: true,\r\n      html5player: 'dummy',\r\n      related_videos: []\r\n    };\r\n\r\n    console.log(`✅ Converted to ytdl format: ${ytdlFormats.length} formats available`);\r\n    console.log(`🎯 Best audio format: itag ${bestFormat.itag} (${bestFormat.audioQuality})`);\r\n    \r\n    return ytdlInfo;\r\n  }\r\n} ","import axios from 'axios';\r\nimport { YtdlMp3Error } from './utils';\r\n\r\ninterface CipherData {\r\n  s?: string;\r\n  url?: string;\r\n  sp?: string;\r\n}\r\n\r\nexport class SignatureDecryptor {\r\n  private static playerJsCache = new Map<string, string>();\r\n  private static decryptFunctionCache = new Map<string, Function>();\r\n\r\n  /**\r\n   * Decrypt a signature cipher and return the complete URL\r\n   */\r\n  static async decryptSignature(cipher: string, videoId: string): Promise<string> {\r\n    try {\r\n      console.log('🔍 Parsing cipher:', cipher.substring(0, 100) + '...');\r\n      const cipherData = this.parseCipher(cipher);\r\n      console.log('📊 Cipher data:', { \r\n        hasSignature: !!cipherData.s, \r\n        hasUrl: !!cipherData.url,\r\n        sp: cipherData.sp \r\n      });\r\n      \r\n      if (!cipherData.s) {\r\n        // No signature to decrypt, return URL as-is\r\n        console.log('✅ No signature needed, returning URL as-is');\r\n        return cipherData.url || '';\r\n      }\r\n\r\n      console.log('🔑 Getting decryption function...');\r\n      // Get the decryption function\r\n      const decryptFunction = await this.getDecryptFunction(videoId);\r\n      \r\n      console.log('🔐 Decrypting signature...');\r\n      // Decrypt the signature\r\n      const decryptedSignature = decryptFunction(cipherData.s);\r\n      console.log('✅ Signature decrypted, length:', decryptedSignature.length);\r\n      \r\n      // Build the final URL\r\n      const url = new URL(cipherData.url!);\r\n      url.searchParams.set(cipherData.sp || 'sig', decryptedSignature);\r\n      \r\n      const finalUrl = url.toString();\r\n      console.log('🎯 Final URL created, length:', finalUrl.length);\r\n      return finalUrl;\r\n    } catch (error) {\r\n      console.error('❌ Signature decryption error:', error);\r\n      throw new YtdlMp3Error(`Signature decryption failed: ${error}`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Parse cipher string into components\r\n   */\r\n  private static parseCipher(cipher: string): CipherData {\r\n    const params = new URLSearchParams(cipher);\r\n    return {\r\n      s: params.get('s') || undefined,\r\n      url: params.get('url') || undefined,\r\n      sp: params.get('sp') || undefined,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Get or create decryption function for a video\r\n   */\r\n  private static async getDecryptFunction(videoId: string): Promise<Function> {\r\n    try {\r\n      // Get player JS URL\r\n      const playerJsUrl = await this.getPlayerJsUrl(videoId);\r\n      \r\n      // Check cache first\r\n      const cacheKey = this.extractPlayerVersion(playerJsUrl);\r\n      if (this.decryptFunctionCache.has(cacheKey)) {\r\n        return this.decryptFunctionCache.get(cacheKey)!;\r\n      }\r\n\r\n      // Download and parse player JS\r\n      const playerJs = await this.downloadPlayerJs(playerJsUrl);\r\n      \r\n      // Extract decryption function\r\n      const decryptFunction = this.extractDecryptFunction(playerJs);\r\n      \r\n      // Cache the function\r\n      this.decryptFunctionCache.set(cacheKey, decryptFunction);\r\n      \r\n      return decryptFunction;\r\n    } catch (error) {\r\n      throw new YtdlMp3Error(`Failed to get decrypt function: ${error}`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get player JS URL from video page\r\n   */\r\n  private static async getPlayerJsUrl(videoId: string): Promise<string> {\r\n    const watchUrl = `https://www.youtube.com/watch?v=${videoId}`;\r\n    \r\n    const response = await axios.get(watchUrl, {\r\n      headers: {\r\n        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'\r\n      }\r\n    });\r\n\r\n    // Look for player JS URL in the page\r\n    const playerJsMatch = response.data.match(/\"jsUrl\":\"([^\"]+)\"/);\r\n    if (!playerJsMatch) {\r\n      throw new YtdlMp3Error('Could not find player JS URL');\r\n    }\r\n\r\n    let playerJsUrl = playerJsMatch[1].replace(/\\\\u0026/g, '&').replace(/\\\\\\//g, '/');\r\n    \r\n    // Ensure it's a complete URL\r\n    if (playerJsUrl.startsWith('//')) {\r\n      playerJsUrl = 'https:' + playerJsUrl;\r\n    } else if (playerJsUrl.startsWith('/')) {\r\n      playerJsUrl = 'https://www.youtube.com' + playerJsUrl;\r\n    }\r\n\r\n    return playerJsUrl;\r\n  }\r\n\r\n  /**\r\n   * Download player JavaScript code\r\n   */\r\n  private static async downloadPlayerJs(playerJsUrl: string): Promise<string> {\r\n    const cacheKey = this.extractPlayerVersion(playerJsUrl);\r\n    \r\n    if (this.playerJsCache.has(cacheKey)) {\r\n      return this.playerJsCache.get(cacheKey)!;\r\n    }\r\n\r\n    const response = await axios.get(playerJsUrl, {\r\n      headers: {\r\n        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'\r\n      }\r\n    });\r\n\r\n    this.playerJsCache.set(cacheKey, response.data);\r\n    return response.data;\r\n  }\r\n\r\n  /**\r\n   * Extract player version from URL for caching\r\n   */\r\n  private static extractPlayerVersion(playerJsUrl: string): string {\r\n    const match = playerJsUrl.match(/player[_-]([a-zA-Z0-9_-]+)/);\r\n    return match ? match[1] : 'unknown';\r\n  }\r\n\r\n  /**\r\n   * Extract signature decryption function from player JS\r\n   */\r\n  private static extractDecryptFunction(playerJs: string): Function {\r\n    try {\r\n      // Find the signature decryption function\r\n      // Pattern 1: Find main decryption function\r\n      let funcName = this.findDecryptFunctionName(playerJs);\r\n      if (!funcName) {\r\n        throw new Error('Could not find decrypt function name');\r\n      }\r\n\r\n      // Extract the function body with multiple patterns\r\n      const funcPatterns = [\r\n        // Pattern 1: Classic pattern\r\n        new RegExp(`${this.escapeRegExp(funcName)}=function\\\\(a\\\\)\\\\{(.+?)\\\\};`),\r\n        \r\n        // Pattern 2: Modern pattern with flexible parameters\r\n        new RegExp(`${this.escapeRegExp(funcName)}=function\\\\([^)]*\\\\)\\\\{(.+?)\\\\};`),\r\n        \r\n        // Pattern 3: Arrow function style\r\n        new RegExp(`${this.escapeRegExp(funcName)}=\\\\([^)]*\\\\)=>\\\\{(.+?)\\\\}`),\r\n        \r\n        // Pattern 4: More flexible function declaration\r\n        new RegExp(`${this.escapeRegExp(funcName)}\\\\s*=\\\\s*function\\\\s*\\\\([^)]*\\\\)\\\\s*\\\\{([^}]+(?:\\\\{[^}]*\\\\}[^}]*)*)\\\\}`),\r\n        \r\n        // Pattern 5: Without semicolon terminator\r\n        new RegExp(`${this.escapeRegExp(funcName)}=function\\\\([^)]*\\\\)\\\\{(.+?)\\\\}(?=,|\\\\n|$)`)\r\n      ];\r\n\r\n      let funcMatch = null;\r\n      let funcBody = null;\r\n      \r\n      for (let i = 0; i < funcPatterns.length; i++) {\r\n        funcMatch = playerJs.match(funcPatterns[i]);\r\n        if (funcMatch) {\r\n          funcBody = funcMatch[1];\r\n          console.log(`✅ Extracted function body with pattern ${i + 1}, length: ${funcBody.length}`);\r\n          break;\r\n        }\r\n      }\r\n      \r\n      if (!funcMatch || !funcBody) {\r\n        // Last resort: try to find any function with the name\r\n        console.log('🔍 Trying fallback extraction...');\r\n        const fallbackPattern = new RegExp(`${this.escapeRegExp(funcName)}[^{]*\\\\{([^}]*)\\\\}`);\r\n        const fallbackMatch = playerJs.match(fallbackPattern);\r\n        if (fallbackMatch) {\r\n          funcBody = fallbackMatch[1];\r\n          console.log('🔧 Used fallback extraction, length:', funcBody.length);\r\n        } else {\r\n          throw new Error('Could not extract decrypt function body');\r\n        }\r\n      }\r\n\r\n      // Find helper object with multiple patterns\r\n      console.log('🔍 Function body:', funcBody);\r\n      \r\n      const helperPatterns = [\r\n        /(\\w+)\\.\\w+\\(a,\\d+\\)/,\r\n        /(\\w+)\\.\\w+\\(\\w+,\\d+\\)/,\r\n        /(\\w+)\\[(\\w+)\\]\\(\\w+,\\d+\\)/,\r\n        /(\\w+)\\[['\"](\\w+)['\"]\\]\\(\\w+,\\d+\\)/\r\n      ];\r\n      \r\n      let helperMatch = null;\r\n      for (const pattern of helperPatterns) {\r\n        helperMatch = funcBody.match(pattern);\r\n        if (helperMatch) {\r\n          console.log('🎯 Found helper reference:', helperMatch[0]);\r\n          break;\r\n        }\r\n      }\r\n      \r\n      if (!helperMatch) {\r\n        throw new Error('Could not find helper object');\r\n      }\r\n\r\n      const helperName = helperMatch[1];\r\n      console.log('🔧 Looking for helper object:', helperName);\r\n      \r\n      // Try multiple patterns for helper object extraction\r\n      const helperObjPatterns = [\r\n        new RegExp(`var ${this.escapeRegExp(helperName)}=\\\\{(.+?)\\\\};`, 's'),\r\n        new RegExp(`${this.escapeRegExp(helperName)}=\\\\{(.+?)\\\\}`, 's'),\r\n        new RegExp(`var ${this.escapeRegExp(helperName)}=\\\\{([^}]+(?:\\\\{[^}]*\\\\}[^}]*)*)\\\\}`, 's'),\r\n        new RegExp(`${this.escapeRegExp(helperName)}:\\\\{(.+?)\\\\}`, 's')\r\n      ];\r\n      \r\n      let helperObjMatch = null;\r\n      for (let i = 0; i < helperObjPatterns.length; i++) {\r\n        helperObjMatch = playerJs.match(helperObjPatterns[i]);\r\n        if (helperObjMatch) {\r\n          console.log(`✅ Found helper object with pattern ${i + 1}, length: ${helperObjMatch[1].length}`);\r\n          break;\r\n        }\r\n      }\r\n      \r\n      if (!helperObjMatch) {\r\n        throw new Error('Could not extract helper object');\r\n      }\r\n\r\n      // Build and return the decryption function\r\n      return this.buildDecryptFunction(funcBody, helperName, helperObjMatch[1]);\r\n    } catch (error) {\r\n      throw new YtdlMp3Error(`Failed to extract decrypt function: ${error}`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Find the name of the main decryption function\r\n   */\r\n  private static findDecryptFunctionName(playerJs: string): string | null {\r\n    console.log('🔍 Searching for signature decryption function...');\r\n    \r\n    // Look for functions that actually manipulate signatures (split, join, reverse, splice)\r\n    const signaturePatterns = [\r\n      // Pattern 1: Function that splits empty string and joins\r\n      /(\\w+)=function\\([^)]*\\)\\{[^}]*\\.split\\(\\s*['\"]{2}\\s*\\)[^}]*\\.join\\(/,\r\n      \r\n      // Pattern 2: Function with signature transformation operations\r\n      /(\\w+)=function\\([^)]*\\)\\{[^}]*split[^}]*reverse[^}]*join/,\r\n      /(\\w+)=function\\([^)]*\\)\\{[^}]*split[^}]*splice[^}]*join/,\r\n      \r\n      // Pattern 3: Look for function references in signature cipher context\r\n      /set\\([^,]+\\s*,\\s*encodeURIComponent\\s*\\(\\s*(\\w+)\\(/,\r\n      /\\.signature\\s*=\\s*(\\w+)\\(/,\r\n      \r\n      // Pattern 4: Modern patterns that handle array manipulation\r\n      /(\\w+)=function\\([^)]*\\)\\{.*?=.*?\\.split\\(['\"]{2}\\).*?\\.join\\(['\"]{2}\\)/,\r\n    ];\r\n\r\n    for (let i = 0; i < signaturePatterns.length; i++) {\r\n      const pattern = signaturePatterns[i];\r\n      const matches = playerJs.match(pattern);\r\n      if (matches) {\r\n        const funcName = matches[1];\r\n        \r\n        // Verify this function actually manipulates signatures\r\n        const funcPattern = new RegExp(`${this.escapeRegExp(funcName)}=function\\\\([^)]*\\\\)\\\\{([^}]+)\\\\}`);\r\n        const funcBodyMatch = playerJs.match(funcPattern);\r\n        \r\n        if (funcBodyMatch) {\r\n          const body = funcBodyMatch[1];\r\n          // Check if it contains signature manipulation operations\r\n          if (body.includes('split') && body.includes('join') && \r\n              (body.includes('reverse') || body.includes('splice') || body.includes('.'))) {\r\n            console.log(`🎯 Found signature function with pattern ${i + 1}:`, funcName);\r\n            console.log(`📋 Function body preview:`, body.substring(0, 100) + '...');\r\n            return funcName;\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    // Fallback: search for any function containing signature manipulation\r\n    console.log('🔍 Trying comprehensive search...');\r\n    const allFunctions = playerJs.match(/(\\w+)=function\\([^)]*\\)\\{[^}]+\\}/g);\r\n    if (allFunctions) {\r\n      for (const func of allFunctions) {\r\n        const nameMatch = func.match(/(\\w+)=function/);\r\n        if (nameMatch && func.includes('split') && func.includes('join')) {\r\n          const funcName = nameMatch[1];\r\n          console.log('🔧 Found potential signature function:', funcName);\r\n          return funcName;\r\n        }\r\n      }\r\n    }\r\n\r\n    return null;\r\n  }\r\n\r\n  /**\r\n   * Build the actual decryption function\r\n   */\r\n  private static buildDecryptFunction(funcBody: string, helperName: string, helperObj: string): Function {\r\n    // Parse helper functions\r\n    const helper: any = {};\r\n    const helperMatches = helperObj.match(/(\\w+):function\\([^)]*\\)\\{([^}]+)\\}/g);\r\n    \r\n    if (helperMatches) {\r\n      for (const match of helperMatches) {\r\n        const [, name, body] = match.match(/(\\w+):function\\([^)]*\\)\\{([^}]+)\\}/)!;\r\n        \r\n        if (body.includes('reverse')) {\r\n          helper[name] = (a: string[]) => a.reverse();\r\n        } else if (body.includes('splice')) {\r\n          helper[name] = (a: string[], b: number) => a.splice(0, b);\r\n        } else {\r\n          // Swap function\r\n          helper[name] = (a: string[], b: number) => {\r\n            const c = a[0];\r\n            a[0] = a[b % a.length];\r\n            a[b % a.length] = c;\r\n          };\r\n        }\r\n      }\r\n    }\r\n\r\n    // Return the decryption function\r\n    return (signature: string) => {\r\n      const a = signature.split('');\r\n      \r\n      // Execute the function body\r\n      try {\r\n        // Parse and execute each operation\r\n        const operations = funcBody.split(';');\r\n        for (const op of operations) {\r\n          if (op.includes(`${helperName}.`)) {\r\n            const opMatch = op.match(new RegExp(`${helperName}\\\\.(\\\\w+)\\\\(a,(\\\\d+)\\\\)`));\r\n            if (opMatch) {\r\n              const [, funcName, param] = opMatch;\r\n              if (helper[funcName]) {\r\n                helper[funcName](a, parseInt(param));\r\n              }\r\n            }\r\n          }\r\n        }\r\n        \r\n        return a.join('');\r\n      } catch (error) {\r\n        throw new YtdlMp3Error(`Signature decryption execution failed: ${error}`);\r\n      }\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Escape special regex characters\r\n   */\r\n  private static escapeRegExp(string: string): string {\r\n    return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n  }\r\n\r\n  /**\r\n   * Clear caches (useful for testing or memory management)\r\n   */\r\n  static clearCaches(): void {\r\n    this.playerJsCache.clear();\r\n    this.decryptFunctionCache.clear();\r\n  }\r\n} ","[\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1755942743.911978,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"VISITOR_INFO1_LIVE\",\n    \"path\": \"/\",\n    \"sameSite\": \"no_restriction\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"HvV4_XdLDrQ\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1755942743.912218,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"VISITOR_PRIVACY_METADATA\",\n    \"path\": \"/\",\n    \"sameSite\": \"no_restriction\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"CgJVUxIEGgAgEA%3D%3D\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1775413520.876414,\n    \"hostOnly\": false,\n    \"httpOnly\": false,\n    \"name\": \"PREF\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"f4=4000000&f6=40000000&tz=America.Los_Angeles&f7=100\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774147424.40209,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"LOGIN_INFO\",\n    \"path\": \"/\",\n    \"sameSite\": \"no_restriction\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"AFmmF2swRQIhAIAqaR2rmI1K5BaW-74sl7QLN82zG8PI0fuPqxZF7ZOnAiBt1cQtabRTBRw6RW67BqIXv9Q45pDJFVov-Ck7ia7VEA:QUQ3MjNmd2RsZmZVUTl3UVhsZXdLQ3dWeHJxbUt1OHA4dEowaEE0WGEzYzhVQUNHeWM5SXJadFViTXdlQkhvbG95QXNGSDU1NTdmc0s3d0lmRnk0aVJOTmFkVGlqN1k2WHZwakk0bW5wVkpMMmw5dHNjak1xVTNBQk5ZUlozcEI2elFTRklCd0c3N1I3TE90b1ZJX3ZfZjB4dmZtS3IxalNn\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774343879.530732,\n    \"hostOnly\": false,\n    \"httpOnly\": false,\n    \"name\": \"SID\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": false,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"g.a000twi7ME0jvjv-jaEvqjKqXn7QHqI4ikx97pmuBTjJpPGKojy1qlfH8WAcMJAq_YSUdSrOSwACgYKAWcSARcSFQHGX2Mito_-wAlmk6_nFejQX_xuURoVAUF8yKr4iauhXVFYrwCSbWe8lOUp0076\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774343879.530734,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"__Secure-1PSID\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"g.a000twi7ME0jvjv-jaEvqjKqXn7QHqI4ikx97pmuBTjJpPGKojy1jdbKkomXUPLEx7LzzKd6cQACgYKAbsSARcSFQHGX2MiVTLPGtmZuLfCX9C8jlUxUBoVAUF8yKrJ3jEqGwa44xKdBEXqHj9s0076\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774343879.530735,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"__Secure-3PSID\",\n    \"path\": \"/\",\n    \"sameSite\": \"no_restriction\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"g.a000twi7ME0jvjv-jaEvqjKqXn7QHqI4ikx97pmuBTjJpPGKojy1PaBzFFl3-zR8oEOc8BeUdAACgYKAQoSARcSFQHGX2MiSA9Qwjk9-1axioDpKWIftxoVAUF8yKpgU29tqL5mvVTC8iUjfTJT0076\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774343879.530737,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"HSID\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": false,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"AVV1l0o_KhDh5mbT4\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774343879.530738,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"SSID\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"ABig9HYygvRwc-eu_\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774343879.530738,\n    \"hostOnly\": false,\n    \"httpOnly\": false,\n    \"name\": \"APISID\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": false,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"eWq4DfPzGUJwyG0j/AxqxD5EH9LLh5uJFd\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774343879.530739,\n    \"hostOnly\": false,\n    \"httpOnly\": false,\n    \"name\": \"SAPISID\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"7ImLNyWJptdDOCmy/A93k3epPyLwt3M5D7\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774343879.530741,\n    \"hostOnly\": false,\n    \"httpOnly\": false,\n    \"name\": \"__Secure-1PAPISID\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"7ImLNyWJptdDOCmy/A93k3epPyLwt3M5D7\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1774343879.530749,\n    \"hostOnly\": false,\n    \"httpOnly\": false,\n    \"name\": \"__Secure-3PAPISID\",\n    \"path\": \"/\",\n    \"sameSite\": \"no_restriction\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"7ImLNyWJptdDOCmy/A93k3epPyLwt3M5D7\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1772449941.610982,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"__Secure-1PSIDTS\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"sidts-CjEBEJ3XVzJW2NNTsZ73JR6xhNTpdRF8LxNJRA0G8WWz6bnQUsgt-NrbYrGgtPsfo63_EAA\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1772449941.611258,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"__Secure-3PSIDTS\",\n    \"path\": \"/\",\n    \"sameSite\": \"no_restriction\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"sidts-CjEBEJ3XVzJW2NNTsZ73JR6xhNTpdRF8LxNJRA0G8WWz6bnQUsgt-NrbYrGgtPsfo63_EAA\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1772450361.288699,\n    \"hostOnly\": false,\n    \"httpOnly\": false,\n    \"name\": \"SIDCC\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": false,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"AKEyXzUxFivTaAPxVWKZckDv8JZojMGMEC_rQboJry465HWDX1qmIAdyFLNO_3efa-AMvZ2gdXM\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1772450361.288992,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"__Secure-1PSIDCC\",\n    \"path\": \"/\",\n    \"sameSite\": \"unspecified\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"AKEyXzWRhT9cIylE55GbVEx5GMd2wtCiuoLaSk2-5NZnZCkiCofL6LbQBaIIXAymSZhi3ZTSjg\"\n  },\n  {\n    \"domain\": \".youtube.com\",\n    \"expirationDate\": 1772450361.289134,\n    \"hostOnly\": false,\n    \"httpOnly\": true,\n    \"name\": \"__Secure-3PSIDCC\",\n    \"path\": \"/\",\n    \"sameSite\": \"no_restriction\",\n    \"secure\": true,\n    \"session\": false,\n    \"storeId\": \"0\",\n    \"value\": \"AKEyXzUoJ1qyk4fWp-Dt04ArHIXg9hIp87GtYmiQ1BaDmZbebsAtVX6M76s1QrtW3yKhiReoRQ\"\n  }\n]\n","import { Cookie } from '@distube/ytdl-core';\r\nimport { YouTubeSession, YouTubeSessionOptions } from './YouTubeSession';\r\nimport { YtdlMp3Error } from './utils';\r\nimport fs from 'fs';\r\nimport path from 'path';\r\n\r\nexport interface SessionConfig {\r\n  cookies: Cookie[];\r\n  userAgent?: string;\r\n}\r\n\r\nexport class SessionManager {\r\n  private static readonly DEFAULT_COOKIES_PATH = path.join(process.cwd(), 'src', 'cookies.json');\r\n  \r\n  /**\r\n   * Load session from cookies file\r\n   */\r\n  static loadFromFile(cookiesPath?: string): YouTubeSession {\r\n    const filePath = cookiesPath || this.DEFAULT_COOKIES_PATH;\r\n    \r\n    if (!fs.existsSync(filePath)) {\r\n      throw new YtdlMp3Error(`Cookies file not found: ${filePath}`);\r\n    }\r\n\r\n    try {\r\n      const cookies = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Cookie[];\r\n      console.log(`📂 Loaded ${cookies.length} cookies from ${filePath}`);\r\n      return this.createSession({ cookies });\r\n    } catch (error) {\r\n      throw new YtdlMp3Error(`Failed to parse cookies file: ${error}`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Create session from configuration\r\n   */\r\n  static createSession(config: SessionConfig): YouTubeSession {\r\n    if (!config.cookies || config.cookies.length === 0) {\r\n      throw new YtdlMp3Error('No cookies provided');\r\n    }\r\n\r\n    const options: YouTubeSessionOptions = {\r\n      cookies: config.cookies,\r\n      userAgent: config.userAgent,\r\n    };\r\n\r\n    return new YouTubeSession(options);\r\n  }\r\n\r\n  /**\r\n   * Validate session by testing a video\r\n   */\r\n  static async validateSession(session: YouTubeSession, testUrl?: string): Promise<boolean> {\r\n    const defaultTestUrl = 'https://www.youtube.com/watch?v=jNQXAC9IVRw'; // \"Me at the zoo\" - first YouTube video\r\n    const urlToTest = testUrl || defaultTestUrl;\r\n\r\n    try {\r\n      const videoInfo = await session.getVideoInfo(urlToTest);\r\n      console.log(`✅ Session validation successful: ${videoInfo.title}`);\r\n      return true;\r\n    } catch (error) {\r\n      console.error('❌ Session validation failed:', error);\r\n      return false;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get session health information\r\n   */\r\n  static getSessionHealth(cookies: Cookie[]): {\r\n    hasAuthentication: boolean;\r\n    hasVisitorData: boolean;\r\n    expiredCookies: string[];\r\n    validCookies: number;\r\n    recommendations: string[];\r\n  } {\r\n    const now = Date.now() / 1000;\r\n    const expiredCookies: string[] = [];\r\n    let validCookies = 0;\r\n\r\n    for (const cookie of cookies) {\r\n      if (cookie.expirationDate && cookie.expirationDate < now) {\r\n        expiredCookies.push(cookie.name);\r\n      } else {\r\n        validCookies++;\r\n      }\r\n    }\r\n\r\n    const hasAuthentication = cookies.some(c => \r\n      ['LOGIN_INFO', 'SID', 'HSID', 'SSID', 'APISID', 'SAPISID'].includes(c.name)\r\n    );\r\n\r\n    const hasVisitorData = cookies.some(c => \r\n      ['VISITOR_INFO1_LIVE', 'VISITOR_PRIVACY_METADATA'].includes(c.name)\r\n    );\r\n\r\n    const recommendations = this.getSessionRecommendations({\r\n      hasAuthentication,\r\n      hasVisitorData,\r\n      expiredCookies,\r\n      validCookies\r\n    });\r\n\r\n    return {\r\n      hasAuthentication,\r\n      hasVisitorData,\r\n      expiredCookies,\r\n      validCookies,\r\n      recommendations\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Create a test session (uses cookies from file or creates minimal one)\r\n   */\r\n  static createTestSession(): YouTubeSession {\r\n    try {\r\n      return this.loadFromFile();\r\n    } catch (error) {\r\n      console.warn('⚠️  Could not load from default cookies file, creating minimal session');\r\n      \r\n      // Create a minimal session for testing (not recommended for production)\r\n      const minimalCookies: Cookie[] = [\r\n        {\r\n          name: 'VISITOR_INFO1_LIVE',\r\n          value: 'test_visitor_' + Date.now(),\r\n          domain: '.youtube.com',\r\n          path: '/',\r\n          secure: true,\r\n          httpOnly: false,\r\n          expirationDate: Date.now() / 1000 + 86400 // 24 hours from now\r\n        }\r\n      ];\r\n      \r\n      console.warn('⚠️  Using minimal test session - not recommended for production');\r\n      return this.createSession({ cookies: minimalCookies });\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Export current cookies to file\r\n   */\r\n  static exportCookies(cookies: Cookie[], outputPath: string): void {\r\n    try {\r\n      fs.writeFileSync(outputPath, JSON.stringify(cookies, null, 2));\r\n      console.log(`✅ Cookies exported to: ${outputPath}`);\r\n    } catch (error) {\r\n      throw new YtdlMp3Error(`Failed to export cookies: ${error}`);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Clean expired cookies from array\r\n   */\r\n  static cleanExpiredCookies(cookies: Cookie[]): Cookie[] {\r\n    const now = Date.now() / 1000;\r\n    const validCookies = cookies.filter(cookie => \r\n      !cookie.expirationDate || cookie.expirationDate > now\r\n    );\r\n    \r\n    console.log(`🧹 Cleaned ${cookies.length - validCookies.length} expired cookies`);\r\n    return validCookies;\r\n  }\r\n\r\n  /**\r\n   * Get recommendations for improving session reliability\r\n   */\r\n  static getSessionRecommendations(health: {\r\n    hasAuthentication: boolean;\r\n    hasVisitorData: boolean;\r\n    expiredCookies: string[];\r\n    validCookies: number;\r\n  }): string[] {\r\n    const recommendations: string[] = [];\r\n\r\n    if (!health.hasAuthentication) {\r\n      recommendations.push('🔐 Add authentication cookies (LOGIN_INFO, SID, etc.) for better reliability');\r\n    }\r\n\r\n    if (!health.hasVisitorData) {\r\n      recommendations.push('👁️  Add VISITOR_INFO1_LIVE cookie for improved session tracking');\r\n    }\r\n\r\n    if (health.expiredCookies.length > 0) {\r\n      recommendations.push(`⏰ Update expired cookies: ${health.expiredCookies.join(', ')}`);\r\n    }\r\n\r\n    if (health.validCookies < 5) {\r\n      recommendations.push('📊 Consider adding more cookies for better session stability');\r\n    }\r\n\r\n    if (recommendations.length === 0) {\r\n      recommendations.push('✅ Your session looks healthy!');\r\n    }\r\n\r\n    return recommendations;\r\n  }\r\n\r\n  /**\r\n   * Quick health check and display\r\n   */\r\n  static displaySessionHealth(cookies: Cookie[]): void {\r\n    const health = this.getSessionHealth(cookies);\r\n    \r\n    console.log('\\n📋 Session Health Report:');\r\n    console.log(`🔐 Authentication: ${health.hasAuthentication ? '✅' : '❌'}`);\r\n    console.log(`👁️  Visitor Data: ${health.hasVisitorData ? '✅' : '❌'}`);\r\n    console.log(`🍪 Valid Cookies: ${health.validCookies}`);\r\n    console.log(`⏰ Expired Cookies: ${health.expiredCookies.length}`);\r\n    \r\n    if (health.recommendations.length > 0) {\r\n      console.log('\\n💡 Recommendations:');\r\n      health.recommendations.forEach(rec => console.log(`  ${rec}`));\r\n    }\r\n    console.log('');\r\n  }\r\n\r\n  /**\r\n   * Create session with automatic health check\r\n   */\r\n  static createSessionWithHealthCheck(cookies: Cookie[]): YouTubeSession {\r\n    console.log('🔍 Performing session health check...');\r\n    this.displaySessionHealth(cookies);\r\n    \r\n    const cleanCookies = this.cleanExpiredCookies(cookies);\r\n    return this.createSession({ cookies: cleanCookies });\r\n  }\r\n} ","import { Command } from 'commander';\n\n//import { description, name, version } from '../package.json';\n\nimport { Downloader } from './Downloader';\nimport { SessionManager } from './SessionManager';\nimport { YtdlMp3Error } from './utils';\n\nexport async function main() {\n  const program = new Command();\n  program.name('audiodl-mp3');\n  program.description('Reliable YouTube to MP3 downloader with cookie-based authentication');\n  program.version('4.0.1');\n  program.allowExcessArguments(false);\n  program.argument('<url>', 'url of video to download');\n  program.option('-o --output-dir <path>', 'path to output directory', Downloader.defaultDownloadsDir);\n  program.option('-c --cookies-file <path>', 'path to cookies JSON file', 'src/cookies.json');\n  program.option('-n --no-get-tags', 'skip extracting/applying id3 tags');\n  program.option('-v --verify-tags', 'verify id3 tags fetched from itunes');\n  program.option('--health-check', 'perform session health check before download');\n  program.option('--verbose', 'enable verbose mode');\n  \n  // Parse command line arguments\n  program.parse(process.argv);\n  \n  const options = program.opts();\n  \n  try {\n    // Load cookies from file\n    let session;\n    try {\n      session = SessionManager.loadFromFile(options.cookiesFile);\n    } catch (error) {\n      console.error(`❌ Failed to load cookies from ${options.cookiesFile}`);\n      console.error('💡 Make sure you have a valid cookies.json file');\n      console.error('📖 See USAGE_GUIDE.md for setup instructions');\n      process.exit(1);\n    }\n\n    // Perform health check if requested\n    if (options.healthCheck) {\n      const cookies = JSON.parse(require('fs').readFileSync(options.cookiesFile, 'utf8'));\n      SessionManager.displaySessionHealth(cookies);\n      \n      const isValid = await SessionManager.validateSession(session);\n      if (!isValid) {\n        console.error('❌ Session validation failed. Please check your cookies.');\n        process.exit(1);\n      }\n      console.log('✅ Session validation successful!\\n');\n    }\n\n    // Create downloader with cookies\n    const cookies = JSON.parse(require('fs').readFileSync(options.cookiesFile, 'utf8'));\n    const downloader = new Downloader({\n      outputDir: options.outputDir,\n      getTags: !options.noGetTags,\n      verifyTags: options.verifyTags,\n      cookies: cookies,\n      retries: 3\n    });\n\n    const url = program.args[0];\n    if (!url) {\n      throw new YtdlMp3Error('URL is required');\n    }\n\n    console.log(`🎯 Starting download: ${url}\\n`);\n    await downloader.downloadSong(url);\n    \n  } catch (err) {\n    if (err instanceof YtdlMp3Error) {\n      if (options.verbose) {\n        console.error(err.cause);\n        console.error(err.stack);\n      }\n      console.error(`\\n❌ ERROR: ${err.message}`);\n      process.exit(1);\n    }\n    throw err;\n  }\n}\n\n// Only run main when this file is executed directly (CLI usage)\nif (require.main === module) {\n  main().catch(console.error);\n}\n"],"mappings":"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","names":["import_fs","fs","ffmpeg","fs","cp","axios","import_axios","import_axios","axios","axios","fs","ytdl","import_fs","path","os","NodeID3","fs","import_fs","import_path","path","fs","cookies"]}