Get-Download.psm1

#Requires -Version 5.0

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$CachePath = Join-Path -Path $env:APPDATA -ChildPath "Get-Download\PRIVATE"
$YtDlpExe = Join-Path -Path $CachePath -ChildPath "yt-dlp.exe"
$YtDlpDownloadUrl = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe"
$ModulePath = $PSScriptRoot
$GitHubRawUrl = "https://raw.githubusercontent.com/Goodwintek/Get-Download/main"

function Initialize-YtDlp {
    if (Test-Path -Path $YtDlpExe -PathType Leaf) {
        return $true
    }

    if (-not (Test-Path -Path $CachePath -PathType Container)) {
        New-Item -ItemType Directory -Path $CachePath -Force | Out-Null
    }

    try {
        Write-Host "Downloading yt-dlp.exe..."
        Invoke-WebRequest -Uri $YtDlpDownloadUrl -OutFile $YtDlpExe -ErrorAction Stop
        Write-Host "yt-dlp.exe downloaded successfully."
        return $true
    } catch {
        [System.Windows.Forms.MessageBox]::Show("Failed to download yt-dlp.exe: $($_.Exception.Message)", "Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        return $false
    }
}

function Update-GetDownload {
    [CmdletBinding()]
    param()

    $result = [System.Windows.Forms.MessageBox]::Show("This will update Get-Download module and yt-dlp. Continue?", "Update", [System.Windows.Forms.MessageBoxButtons]::YesNo, [System.Windows.Forms.MessageBoxIcon]::Question)
    
    if ($result -ne [System.Windows.Forms.DialogResult]::Yes) {
        return
    }

    try {
        Write-Host "Updating Get-Download module..."

        $psd1Url = "$GitHubRawUrl/Get-Download.psd1"
        $psm1Url = "$GitHubRawUrl/Get-Download.psm1"
        $psd1Path = Join-Path -Path $ModulePath -ChildPath "Get-Download.psd1"
        $psm1Path = Join-Path -Path $ModulePath -ChildPath "Get-Download.psm1"

        Invoke-WebRequest -Uri $psd1Url -OutFile $psd1Path -ErrorAction Stop
        Invoke-WebRequest -Uri $psm1Url -OutFile $psm1Path -ErrorAction Stop

        Write-Host "Module updated successfully."

        if (Test-Path -Path $YtDlpExe -PathType Leaf) {
            Write-Host "Updating yt-dlp.exe..."
            & $YtDlpExe -U | Out-Null
            Write-Host "yt-dlp.exe updated successfully."
        }

        [System.Windows.Forms.MessageBox]::Show("Update complete. Application will restart.", "Success", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)

        $global:RestartApp = $true
    } catch {
        [System.Windows.Forms.MessageBox]::Show("Update failed: $($_.Exception.Message)", "Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
        Write-Host "Update error: $_"
    }
}

function Get-YtDlpArgs {
    param(
        [string]$OutputDir,
        [string]$Url,
        [bool]$AudioOnly,
        [bool]$SslFix,
        [bool]$FfmpegHls,
        [bool]$BotFix
    )

    $argsList = [System.Collections.Generic.List[string]]::new()

    $argsList.Add("--output")
    $argsList.Add("$OutputDir\%(title)s.%(ext)s")

    if ($FfmpegHls) {
        $argsList.Add("--merge-output-format")
        $argsList.Add("mp4")
    } elseif (-not $Url.EndsWith(".m3u8")) {
        $argsList.Add("--embed-metadata")
    }

    if ($AudioOnly) { 
        $argsList.Add("-x") 
    }
    if ($SslFix) { 
        $argsList.Add("--legacy-server-connect")
        $argsList.Add("--no-check-certificate")
    }
    if ($FfmpegHls) { 
        $argsList.Add("--downloader")
        $argsList.Add("m3u8:ffmpeg")
        $argsList.Add("--downloader-args")
        $argsList.Add("ffmpeg:-protocol_whitelist file,http,https,tcp,tls,crypto")
    }
    if ($BotFix) { 
        $argsList.Add("--extractor-args")
        $argsList.Add("generic:impersonate")
    }
    
    $argsList.Add($Url)

    return $argsList
}

function Get-Download {
    [CmdletBinding()]
    param(
        [string]$DefaultOutputPath = "",
        [switch]$U
    )

    if ($U) {
        Update-GetDownload
        return
    }

    if (-not (Initialize-YtDlp)) {
        return
    }

    if (-not (Test-Path -Path $YtDlpExe -PathType Leaf)) {
        throw "yt-dlp.exe not found at: $YtDlpExe"
    }

    do {
        $global:RestartApp = $false

        $form = New-Object System.Windows.Forms.Form
        $form.Text = "Get-Download"
        $form.Width = 400
        $form.Height = 210
        $form.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen

        $menuStrip = New-Object System.Windows.Forms.MenuStrip
        $helpMenu = New-Object System.Windows.Forms.ToolStripMenuItem
        $helpMenu.Text = "Help"
        $updateItem = New-Object System.Windows.Forms.ToolStripMenuItem
        $updateItem.Text = "Check for Updates"
        $updateItem.Add_Click({ Update-GetDownload })
        $helpMenu.DropDownItems.Add($updateItem) | Out-Null
        $menuStrip.Items.Add($helpMenu) | Out-Null
        $form.Controls.Add($menuStrip)
        $form.MainMenuStrip = $menuStrip

        $urlLabel = New-Object System.Windows.Forms.Label
        $urlLabel.Location = New-Object System.Drawing.Point(10, 35)
        $urlLabel.AutoSize = $true
        $urlLabel.Text = "Playlist URL:"
        $form.Controls.Add($urlLabel)

        $script:urlTextBox = New-Object System.Windows.Forms.TextBox
        $script:urlTextBox.Location = New-Object System.Drawing.Point(100, 32)
        $script:urlTextBox.Width = 245
        $script:urlTextBox.Add_KeyDown({
            if ($_.Control -and $_.KeyCode -eq [System.Windows.Forms.Keys]::A) {
                $script:urlTextBox.SelectAll()
                $_.SuppressKeyPress = $true
            }
        })
        $script:urlTextBox.Add_DoubleClick({
            $script:urlTextBox.SelectAll()
        })
        $form.Controls.Add($script:urlTextBox)

        $clearUrlButton = New-Object System.Windows.Forms.Button
        $clearUrlButton.Location = New-Object System.Drawing.Point(350, 30)
        $clearUrlButton.Width = 30
        $clearUrlButton.Height = 22
        $clearUrlButton.Text = "X"
        $clearUrlButton.Add_Click({
            $script:urlTextBox.Clear()
            $script:urlTextBox.Focus()
        })
        $form.Controls.Add($clearUrlButton)

        $audioOnlyCheckBox = New-Object System.Windows.Forms.CheckBox
        $audioOnlyCheckBox.Location = New-Object System.Drawing.Point(10, 70)
        $audioOnlyCheckBox.AutoSize = $true
        $audioOnlyCheckBox.Text = "Extract Audio Only (-x)"
        $form.Controls.Add($audioOnlyCheckBox)

        $outputLabel = New-Object System.Windows.Forms.Label
        $outputLabel.Location = New-Object System.Drawing.Point(10, 100)
        $outputLabel.AutoSize = $true
        $outputLabel.Text = "Output Folder:"
        $form.Controls.Add($outputLabel)

        $script:outputTextBox = New-Object System.Windows.Forms.TextBox
        $script:outputTextBox.Location = New-Object System.Drawing.Point(100, 97)
        $script:outputTextBox.Width = 200
        if ($DefaultOutputPath) {
            $script:outputTextBox.Text = $DefaultOutputPath
        }
        $form.Controls.Add($script:outputTextBox)

        $browseButton = New-Object System.Windows.Forms.Button
        $browseButton.Location = New-Object System.Drawing.Point(305, 95)
        $browseButton.Text = "Browse"
        $browseButton.Add_Click({
            $folderBrowserDialog = New-Object System.Windows.Forms.FolderBrowserDialog
            $folderBrowserDialog.Description = "Select the output folder"
            if (-not [string]::IsNullOrEmpty($script:outputTextBox.Text) -and (Test-Path $script:outputTextBox.Text)) {
                $folderBrowserDialog.SelectedPath = $script:outputTextBox.Text
            }
            if ($folderBrowserDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
                $script:outputTextBox.Text = $folderBrowserDialog.SelectedPath
            }
        })
        $form.Controls.Add($browseButton)

        $downloadButton = New-Object System.Windows.Forms.Button
        $downloadButton.Location = New-Object System.Drawing.Point(150, 140)
        $downloadButton.Text = "Download"
        $downloadButton.Add_Click({
            $url = $script:urlTextBox.Text
            $outputDir = $script:outputTextBox.Text
            $audioOnly = $audioOnlyCheckBox.Checked

            if ([string]::IsNullOrEmpty($url) -or [string]::IsNullOrEmpty($outputDir)) {
                [System.Windows.Forms.MessageBox]::Show("Please enter a URL and select an output folder.", "Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
                return
            }

            if (-not (Test-Path $outputDir)) {
                try {
                    New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
                } catch {
                    [System.Windows.Forms.MessageBox]::Show("Could not create folder: $outputDir`n$($_.Exception.Message)", "Error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error)
                    return
                }
            }

            # Automated fallback sequence: runs checks one by one until successful
            $attempts = [System.Collections.Generic.List[hashtable]]::new()
            $attempts.Add(@{ Ssl = $false; Ffmpeg = $false; Bot = $false }) # 1. Standard
            $attempts.Add(@{ Ssl = $true;  Ffmpeg = $false; Bot = $false }) # 2. SSL Fix
            $attempts.Add(@{ Ssl = $false; Ffmpeg = $false; Bot = $true  }) # 3. Bot Fix
            $attempts.Add(@{ Ssl = $false; Ffmpeg = $true;  Bot = $false }) # 4. HLS Fix
            $attempts.Add(@{ Ssl = $true;  Ffmpeg = $false; Bot = $true  }) # 5. SSL + Bot Fix
            $attempts.Add(@{ Ssl = $true;  Ffmpeg = $true;  Bot = $true  }) # 6. All Fixes Enabled

            foreach ($attempt in $attempts) {
                $argArray = Get-YtDlpArgs -OutputDir $outputDir -Url $url -AudioOnly $audioOnly -SslFix $attempt.Ssl -FfmpegHls $attempt.Ffmpeg -BotFix $attempt.Bot
                Write-Host "Attempt (SSL fix: $($attempt.Ssl), ffmpeg HLS: $($attempt.Ffmpeg), Bot fix: $($attempt.Bot)): $YtDlpExe $($argArray -join ' ')"

                $proc = Start-Process -FilePath $YtDlpExe -ArgumentList $argArray -NoNewWindow -Wait -PassThru
                if ($proc.ExitCode -eq 0) {
                    Write-Host "Download completed successfully."
                    [System.Windows.Forms.MessageBox]::Show("Download completed successfully!", "Success", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
                    return
                }
                Write-Host "yt-dlp exited with code $($proc.ExitCode). Trying next fallback..."
            }

            [System.Windows.Forms.MessageBox]::Show("All fallback attempts failed. Check the console for details.", "yt-dlp error", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
        })
        $form.Controls.Add($downloadButton)

        $form.ShowDialog() | Out-Null
        $form.Dispose()

        if ($global:RestartApp) {
            Remove-Module Get-Download -Force
            Import-Module (Join-Path -Path $ModulePath -ChildPath "Get-Download.psd1") -Force
            Get-Download
            return
        }
    } while ($global:RestartApp)

    Write-Host ""
}

Export-ModuleMember -Function Get-Download, Update-GetDownload