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
    )

    if ($FfmpegHls) {
        $argString = "--output `"$OutputDir\%(title)s.%(ext)s`" --merge-output-format mp4"
    } else {
        $argString = "--output `"$OutputDir\%(title)s.%(ext)s`" --embed-metadata"
    }
    if ($AudioOnly) { $argString += " -x" }
    if ($SslFix) { $argString += " --legacy-server-connect --no-check-certificate" }
    if ($FfmpegHls) { $argString += " --downloader m3u8:ffmpeg --downloader-args `"ffmpeg:-protocol_whitelist file,http,https,tcp,tls,crypto`"" }
    if ($botFix) { $argString += " --extractor-args `"generic:impersonate`"" }
    $argString += " `"$Url`""

    return $argString
}

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 = 280
        $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)

        $sslFixCheckBox = New-Object System.Windows.Forms.CheckBox
        $sslFixCheckBox.Location = New-Object System.Drawing.Point(10, 95)
        $sslFixCheckBox.AutoSize = $true
        $sslFixCheckBox.Text = "SSL Error Fix (legacy TLS + skip cert check)"
        $form.Controls.Add($sslFixCheckBox)

        $ffmpegHlsCheckBox = New-Object System.Windows.Forms.CheckBox
        $ffmpegHlsCheckBox.Location = New-Object System.Drawing.Point(10, 120)
        $ffmpegHlsCheckBox.AutoSize = $true
        $ffmpegHlsCheckBox.Text = "HLS via ffmpeg (fixes CDN m3u8 SSL/stream errors)"
        $form.Controls.Add($ffmpegHlsCheckBox)

        $botFixCheckBox = New-Object System.Windows.Forms.CheckBox
        $botFixCheckBox.Location = New-Object System.Drawing.Point(10, 145)
        $botFixCheckBox.AutoSize = $true
        $botFixCheckBox.Text = "Bot Error Fix"
        $form.Controls.Add($botFixCheckBox)

        $outputLabel = New-Object System.Windows.Forms.Label
        $outputLabel.Location = New-Object System.Drawing.Point(10, 165)
        $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, 162)
        $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, 160)
        $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, 210)
        $downloadButton.Text = "Download"
            $downloadButton.Add_Click({
        $url = $script:urlTextBox.Text
        $outputDir = $script:outputTextBox.Text
        $audioOnly = $audioOnlyCheckBox.Checked
        $sslFix = $sslFixCheckBox.Checked
        $ffmpegHls = $ffmpegHlsCheckBox.Checked
        $botFix = $botFixCheckBox.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
            }
        }

        $attempts = [System.Collections.Generic.List[hashtable]]::new()
        $attempts.Add(@{ Ssl = $sslFix; Ffmpeg = $ffmpegHls; Bot = $botFix })
        foreach ($candidate in @(@{ Ssl = $true; Ffmpeg = $ffmpegHls; Bot = $botFix }, @{ Ssl = $true; Ffmpeg = $true; Bot = $botFix })) {
            $duplicate = $attempts | Where-Object { $_.Ssl -eq $candidate.Ssl -and $_.Ffmpeg -eq $candidate.Ffmpeg -and $_.Bot -eq $candidate.Bot }
            if (-not $duplicate) { $attempts.Add($candidate) }
        }

        foreach ($attempt in $attempts) {
            $argString = 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 $argString"

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

        [System.Windows.Forms.MessageBox]::Show("All 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 $psd1Path -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