Download-Files-TubesMedia-SearchAndDownloadFilesOnWebsite.psm1

<#
    .EXTERNALHELP Download-Files-TubesMedia-SearchAndDownloadFilesOnWebsite.psm1-Help.xml
#>

function Invoke-FileDownload
{
    param
    (
        [Parameter(Mandatory = $true,
                   Position = 1,
                   HelpMessage = 'Provide correct URL like www.google.com')]
        [uri]$URL,
        [Parameter(Mandatory = $true,
                   Position = 2,
                   HelpMessage = 'Enter filename to search on website, you can use wilcards like: file*name*.jpg')]
        [SupportsWildcards()]
        [string]$Filename,
        [Parameter(HelpMessage = 'Enter full download path to folder.')]
        [string]$Path,
        [switch]$GetLink,
        [switch]$ListFoundedFiles,
        [Parameter(HelpMessage = 'Enter full alternate filename with exstension.')]
        [string]$NewFileName
    )
    
    # $aaa = Invoke-WebRequest -Uri "www.rarlab.com/rar/winrar-x64-622.exe" -MaximumRedirection 0 -ErrorAction SilentlyContinue
    # $aaa.Headers
    
    if ($Filename -eq "*") { return Write-Warning 'Cannot provide only wildcard.' }
    function Get-UrlStatusCode
    {
        param
        (
            [Parameter(Mandatory = $true)]
            [uri]$TestURL
        )
        
        try { (Invoke-WebRequest -Uri $TestURL -UseBasicParsing -DisableKeepAlive -ErrorAction SilentlyContinue).StatusCode }
        catch [Net.WebException] { [int]$_.Exception.Response.StatusCode }
    }
    if ($Filename.Substring(0, 1) -ne "*") { $Filename = "*" + $Filename }
    if (!$Path) { $Path = Get-Location }
    $DownloadWebPage = (Invoke-WebRequest "$URL").AllElements
    $FilesFinded = $DownloadWebPage.href -like "$Filename"
    if (!$FilesFinded) { return Write-Warning "No files found like $Filename" }
    $FileSelection = Split-Path -Path $FilesFinded -Leaf
    if ($FilesFinded.Count -gt 1) { $WEBPath = $FilesFinded.Split([Environment]::NewLine) | Select-Object -First 1 }
    else { $WEBPath = $FilesFinded }
    $statusCode = Get-UrlStatusCode -TestURL "$WEBPath" -ErrorAction SilentlyContinue
    if (($statusCode -eq 0) -or (!$statusCode))
    {
        $WEBPath = "$URL" + $WEBPath
        $statusCode = Get-UrlStatusCode -TestURL "$WEBPath"
        if (($statusCode -eq 0) -or (!$statusCode)) { return Write-Error "Cannot find download link for searched file on the $URL" -Category InvalidResult }
    }
    if ($NewFileName) { $Filename = $NewFileName }
    else { $Filename = Split-Path $WEBPath -Leaf }
    $FullFilePath = Join-Path -Path $Path -ChildPath $Filename
    if ($GetLink -and $ListFoundedFiles) { Write-Warning "-GetLink switch should be used without -ListFoundedFiles switch." }
    if ($GetLink -and (!$ListFoundedFiles)) { return $WEBPath }
    elseif ($ListFoundedFiles)
    {
        if ($FileSelection.Count -eq 1) { Write-Output "Only one file founded:" }
        return $FileSelection
    }
    else
    {
        Invoke-WebRequest -Uri $WEBPath -OutFile $FullFilePath -Method Get
        Write-Output "File downloaded to $FullFilePath"
    }
}

<#
    .EXTERNALHELP Download-Files-TubesMedia-SearchAndDownloadFilesOnWebsite.psm1-Help.xml
#>

function Invoke-MediaDownload
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false,
                   Position = 1,
                   HelpMessage = 'Put pure link from site that you want to download it.')]
        [uri]$URL,
        [Parameter(Position = 4,
                   HelpMessage = 'Use only if first method fails.')]
        [switch]$ytdlMethod,
        [Parameter(Position = 2)]
        [string]$Path = ($env:SystemDrive + $env:HOMEPATH + '\Videos\'),
        [Parameter(Position = 3)]
        [string]$AdditionalArguments,
        [Parameter(Position = 5)]
        [switch]$Help
    )
    
    $WinGetEXE = 'winget.exe'
    $choco = 'choco.exe'
    $Downloader = 'yt-dlp.exe'
    $Downloader2 = 'youtube-dl.exe'
    
    #region InstallNeeded
    function Get-NuGet
    {
        Write-Output "Getting NuGet package manager, answer 'Yes' if asked."
        $Repository = 'PSGallery'
        if (Get-PSRepository | Where-Object { $_.Name -eq $Repository -and $_.InstallationPolicy -ne "Trusted" })
        {
            try
            {
                Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.208 -Force -ErrorAction SilentlyContinue
                Set-PSRepository -Name $Repository -InstallationPolicy Trusted -ErrorAction SilentlyContinue
            }
            catch { throw $_ }
        }
        if (-not (Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue))
        {
            [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
            Set-PSRepository -Name 'PSGallery' -InstallationPolicy Trusted -ErrorAction SilentlyContinue
            Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction SilentlyContinue
            if (-not (Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue)) { Write-Error -Message 'Cannot get NuGet package manager. Please update PowerSHell.' -Category NotInstalled; break }
        }
    }
    function Install-WinGet
    {
        $Xaml = Get-AppxPackage -Name 'Microsoft.UI.Xaml.2.7*' -ErrorAction SilentlyContinue
        if (!($Xaml))
        {
            Write-Output 'Installing Microsoft.UI.Xaml.2.7.1'
            $url = 'https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.1'
            $zipFile = $env:TEMP + '\XamlTemp.zip'
            $tempFolder = New-Item -Path "$env:TEMP\XamlInst\" -ItemType Directory -Force -ErrorAction SilentlyContinue
            Invoke-WebRequest -Uri $url -OutFile $zipFile
            Expand-Archive -Path $zipFile -DestinationPath $env:TEMP\XamlInst\ -Force -ErrorAction SilentlyContinue
            if ([Environment]::Is64BitOperatingSystem)
            {
                Add-AppxPackage -Path $tempFolder\tools\AppX\x64\Release\Microsoft.UI.Xaml.2.7.appx -ForceApplicationShutdown -Update -ErrorAction SilentlyContinue
                Remove-Item -Path $zipFile -Force -ErrorAction SilentlyContinue
                Remove-Item -Path $tempFolder -Force -ErrorAction SilentlyContinue
            }
            else
            {
                Add-AppxPackage -Path $tempFolder\tools\AppX\x86\Release\Microsoft.UI.Xaml.2.7.appx -ForceApplicationShutdown -Update -ErrorAction SilentlyContinue
                Remove-Item -Path $zipFile -Force -ErrorAction SilentlyContinue
                Remove-Item -Path $tempFolder -Force -ErrorAction SilentlyContinue
            }
            if (!($Xaml))
            {
                Write-Warning "Microsoft.UI.Xaml.2.7.1 not installed."
                break
            }
        }
        $hasPackageManager = Get-AppxPackage -name 'Microsoft.DesktopAppInstaller' -ErrorAction SilentlyContinue
        if (!$hasPackageManager -or [version]$hasPackageManager.Version -lt [version]"1.10.0.0")
        {
            Write-Output 'Installing WinGet application manager.'
            [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
            if (!$hasPackageManager) { Add-AppxPackage -Path https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -InstallAllResources -ForceApplicationShutdown }
            else { Add-AppxPackage -Path https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx -ForceUpdateFromAnyVersion -ForceApplicationShutdown }
            $releases_url = 'https://api.github.com/repos/microsoft/winget-cli/releases/latest'
            [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
            $releases = Invoke-RestMethod -uri $releases_url
            $latestRelease = $releases.assets | Where-Object { $_.browser_download_url.EndsWith('msixbundle') } | Select-Object -First 1
            Write-Output "Installing winget from $($latestRelease.browser_download_url)"
            Add-AppxPackage -Path $latestRelease.browser_download_url
            
        }
        if (!(Get-Command -Name 'winget.exe' -ErrorAction SilentlyContinue) -and ($restart))
        {
            Write-Warning '-restart switch is used, computer will restart in 5 secounds...'
            Restart-Computer -Force -Timeout 5
        }
        if (!(Get-Command -CommandType Application -Name winget.exe -ErrorAction SilentlyContinue)) { Write-Warning "WinGet still not active, please try to restart the computer and run this command again.`nIf you have fresh installed Windows please update it first."; break }
    }    
    function Get-InstalledSoftware
    {
        [CmdletBinding(SupportsShouldProcess = $false)]
        [OutputType([System.Management.Automation.PSObject])]
        param (
            [Parameter()]
            [ValidateNotNullOrEmpty()]
            [System.String]$Name
        )
        
        process
        {
            $UninstallKeys = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
            $null = New-PSDrive -Name "HKU" -PSProvider "Registry" -Root "Registry::HKEY_USERS"
            $UninstallKeys += Get-ChildItem -Path "HKU:" -ErrorAction "SilentlyContinue" | `
            Where-Object { $_.Name -match "S-\d-\d+-(\d+-){1,14}\d+$" } | `
            ForEach-Object { "HKU:\$($_.PSChildName)\Software\Microsoft\Windows\CurrentVersion\Uninstall" }
            foreach ($UninstallKey in $UninstallKeys)
            {
                if ($PSBoundParameters.ContainsKey("Name")) { $WhereBlock = { ($_.PSChildName -match "^{[A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}}$") -and ($_.GetValue("DisplayName") -like "$Name*") } }
                else { $WhereBlock = { ($_.PSChildName -match "^{[A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}}$") -and ($_.GetValue("DisplayName")) } }
                $SelectProperties = @(
                    @{ n = "Publisher"; e = { $_.GetValue("Publisher") } },
                    @{ n = "Name"; e = { $_.GetValue("DisplayName") } },
                    @{ n = "Version"; e = { $_.GetValue("DisplayVersion") } },
                    @{ n = "ProductCode"; e = { $_.PSChildName } },
                    @{ n = "BundleCachePath"; e = { $_.GetValue("BundleCachePath") } },
                    @{
                        n = "Architecture"; e = {
                            if ($_.GetValue("DisplayName") -like "*x64*") { "x64" }
                            else { "x86" }
                        }
                    },
                    @{ n = "Release"; e = { if ($_.GetValue("DisplayName") -match [RegEx]"(\d{4})\s+") { $matches[0].Trim(" ") } } },
                    @{ n = "UninstallString"; e = { $_.GetValue("UninstallString") } },
                    @{ n = "QuietUninstallString"; e = { $_.GetValue("QuietUninstallString") } },
                    @{ n = "UninstallKey"; e = { $UninstallKey } }
                )
                
                $params = @{
                    Path        = $UninstallKey
                    ErrorAction = "SilentlyContinue"
                }
                Get-ChildItem @params | Where-Object $WhereBlock | Select-Object -Property $SelectProperties
            }
        }
        end { Remove-PSDrive -Name "HKU" -ErrorAction "SilentlyContinue" }
    }
    function Get-InstalledVcRedist
    {
        [CmdletBinding(SupportsShouldProcess = $false, HelpURI = "https://vcredist.com/get-installedvcredist/")]
        [OutputType([System.Management.Automation.PSObject])]
        param (
            [Parameter(Mandatory = $false)]
            [System.Management.Automation.SwitchParameter]$ExportAll
        )
        
        if ($PSBoundParameters.ContainsKey("ExportAll"))
        {
            Write-Verbose -Message "-ExportAll specified. Exporting all install Visual C++ Redistributables and runtimes."
            $Filter = "(Microsoft Visual C.*).*"
        }
        else { $Filter = "(Microsoft Visual C.*)(\bRedistributable).*" }
        Write-Verbose -Message "Matching installed VcRedists with: '$Filter'."
        $VcRedists = Get-InstalledSoftware | Where-Object { $_.Name -match $Filter }
        Write-Verbose -Message "Add Architecture property to output object."
        $VcRedists | ForEach-Object { if ($_.Name -contains "x64") { $_ | Add-Member -NotePropertyName "Architecture" -NotePropertyValue "x64" } }
        Write-Output -InputObject $VcRedists
    }
    function Test-VcRedist
    {
        $vcr = (Get-InstalledVcRedist).Name -like "*2010*x86*"
        if (!([boolean]$vcr))
        {
            Write-Output 'Installing Microsoft Visual C++ 2010 Service Pack 1 Redistributable Package (x86)'
            $VcSetup = "$env:TEMP\vcredist_x86.exe"
            if (Test-Path $VcSetup -ErrorAction SilentlyContinue) { Remove-Item $VcSetup -Force -Recurse -ErrorAction SilentlyContinue }
            $VcURL = "https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe"
            Invoke-WebRequest -Uri $VcURL -OutFile $VcSetup -Method Get -ErrorAction SilentlyContinue
            Start-Process -FilePath $VcSetup -ArgumentList "/passive /norestart" -Wait -ErrorAction SilentlyContinue
            Remove-Item $VcSetup -Force -ErrorAction SilentlyContinue
            $vcr = (Get-InstalledVcRedist).Name -like "*2010*x86*"
            if (!([boolean]$vcr)) { Write-Warning "Installing Microsoft Visual C++ 2010 Service Pack 1 Redistributable Package (x86) failed.`nPlease update Windows and restart it and run this command again"; break }
        }
    }
    function Test-AllNeeded
    {
        Test-VcRedist
        if (!(Get-Command -CommandType Application -Name winget.exe -ErrorAction SilentlyContinue))
        {
            $checkNuGeT = Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue
            if (!$checkNuGeT) { Get-NuGet }
            else { Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.208 }
            Install-WinGet
            if (!(Get-Command -CommandType Application -Name winget.exe -ErrorAction SilentlyContinue)) { Write-Error 'WinGet not installed, if your Windows is fresh installed, please update it and restart the computer.' -Category NotInstalled }
        }
        if (!(Get-Command -Name $Downloader -ErrorAction SilentlyContinue))
        {
            Write-Output 'Getting yt-dlp video/audio download manager.'
            Start-Process -FilePath $WinGetEXE -ArgumentList "install yt-dlp --accept-package-agreements" -NoNewWindow -Wait
            if (!(Get-Command -Name $Downloader -ErrorAction SilentlyContinue))
            {
                Write-Warning 'yt-dlp.exe not installed, restart the computer and start this command again'
                break
            }
            else { Start-Process -FilePath $Downloader -ArgumentList '-U' -NoNewWindow -Wait }
        }
        if (!(Get-Command -Name $Downloader2 -ErrorAction SilentlyContinue))
        {
            Write-Output 'Getting youtube-dl video/audio download manager.'
            Start-Process -FilePath $choco -ArgumentList "install youtube-dl -y -f" -NoNewWindow -Wait
        }
        if (!(Get-Command -Name $Downloader2 -ErrorAction SilentlyContinue))
        {
            $here = (Get-Location).Path
            Invoke-FileDownload -URL https://yt-dl.org/ -Filename $Downloader2 -Path $here
            Start-Process -FilePath $Downloader2 -ArgumentList '-U' -NoNewWindow -Wait
            if (!(Get-Command -Name $Downloader2 -ErrorAction SilentlyContinue)) { Write-Warning "youtube-dl.exe not installed.`nTry to restart or update Windows then run this command again."; break }
        }
        else { Start-Process -FilePath $Downloader -ArgumentList '-U' -NoNewWindow -Wait }
    }
    #endregion InstallNeeded
    
    #region check
    if (!(Get-Command -Name $Downloader -ErrorAction SilentlyContinue) -or !(Get-Command -Name $Downloader2 -ErrorAction SilentlyContinue))
    {
        if (!(Get-Command -Name $choco -ErrorAction SilentlyContinue))
        {
            if (!(Get-Command -Name choco.exe -ErrorAction SilentlyContinue)) { Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) }
        }
        if (Get-Command -Name $choco -ErrorAction SilentlyContinue)
        {
            Start-Process -FilePath $choco -ArgumentList "install yt-dlp -y -f --nocolor --no-progress" -NoNewWindow -Wait
            Start-Process -FilePath $choco -ArgumentList "install youtube-dl -y -f --nocolor --no-progress" -NoNewWindow -Wait
            if (!(Get-Command -Name $Downloader -ErrorAction SilentlyContinue) -or !(Get-Command -Name $Downloader2 -ErrorAction SilentlyContinue))
            {
                Test-AllNeeded
                if (!(Get-Command -Name $Downloader -ErrorAction SilentlyContinue) -or !(Get-Command -Name $Downloader2 -ErrorAction SilentlyContinue))
                {
                    Write-Error 'Cannot install needed files.' -Category NotInstalled
                    break
                }
            }
        }
        else
        {
            Test-AllNeeded
            if (!(Get-Command -Name $Downloader -ErrorAction SilentlyContinue) -or !(Get-Command -Name $Downloader2 -ErrorAction SilentlyContinue))
            {
                Write-Error 'Cannot install needed files.' -Category NotInstalled
                break
            }
        }
    }
    #endregion check
    
    if (!$URL -and !$Help) { return Write-Error 'Must specify the URL for video download.' -Category OperationStopped }
    if ($Help)
    {
        if ($ytdlMethod)
        {
            Write-Output ''
            Start-Process -FilePath $Downloader2 -ArgumentList "--help" -NoNewWindow -Wait
            Write-Output ''
        }
        else
        {
            Write-Output ''
            Start-Process -FilePath $Downloader -ArgumentList "--help" -NoNewWindow -Wait
            Write-Output ''
        }
        return
    }
    if ($ytdlMethod)
    {
        $Filename = Read-Host -Prompt "File will be dowloaded at $Path`nPlease enter the filename for video file"
        if ($Filename -like '') { return Write-Error "With youtube-dl method filename must be entered." -Category InvalidData }
        $arguments = "$URL -o $Path$Filename.mp4 "
        if ($AdditionalArguments) { $arguments = $arguments + $AdditionalArguments }
        Write-Output ''
        Start-Process -FilePath $Downloader2 -ArgumentList $arguments -NoNewWindow -Wait
        Start-Process -FilePath $Downloader2 -ArgumentList '-U' -WindowStyle Hidden
        Write-Output ''
    }
    else
    {
        $arguments = "$URL -P$Path "
        if ($AdditionalArguments) { $arguments = $arguments + $AdditionalArguments }
        Write-Output ''
        Start-Process -FilePath $Downloader -ArgumentList $arguments -NoNewWindow -Wait
        Start-Process -FilePath $Downloader -ArgumentList '-U' -WindowStyle Hidden
        Write-Output ''
    }
}