基于PowerShell和yt-dlp的简单大批量cookie登录+正常浏览伪装的视频下载器 更新5

A@NAZOrip
A@NAZOrip 9月11日
  • 在其它设备中阅读本文章

❀原创教程,转载本页必须注明链接和作者

❀目前本文档处于发布早期,代码可能会不稳~

因油管河蟹,及不慎手抽而痛失收藏视频,遂慌,借PowerShell bbenc工程代码而凑此物. (文言文太差,尽力了)

使用前,要在设置-->更新和安全-->开发者选项中解除PowerShell的运行限制,如图:
bbenc-ttl5zh.png


代码如下,拷下来用Unicode/UTF-8编码保存为yt-dlp-ops.ps1运行即可(⊙_◎):

Function whereisit($startPath='DESKTOP') {
    #启用System.Windows.Forms选择文件的GUI交互窗,通过SelectedPath将GUI交互窗锁定到桌面文件夹, 效果一般
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
    Add-Type -AssemblyName System.Windows.Forms
    $startPath = New-Object System.Windows.Forms.OpenFileDialog -Property @{ InitialDirectory = [Environment]::GetFolderPath('DESKTOP') }#GUI交互窗锁定到桌面文件夹
    Do {$dInput = $startPath.ShowDialog()} While ($dInput -eq "Cancel") #1.打开选择文件的GUI交互窗, 通过重新打开选择窗来反取消用户的取消操作. 2. 窗口大, TopMost关
    return $startPath.FileName
}
Function whichlocation($startPath='DESKTOP') {
    #启用System.Windows.Forms选择文件夹的GUI交互窗
    Add-Type -AssemblyName System.Windows.Forms
    $startPath = New-Object System.Windows.Forms.FolderBrowserDialog -Property @{ Description="选择路径用的窗口. 拖拽边角可放大以便操作"; SelectedPath=[Environment]::GetFolderPath($startPath); RootFolder='MyComputer'; ShowNewFolderButton=$true }
    #打开选择文件的GUI交互窗, 用Do-While循环拦截误操作(取消/关闭选择窗)
    Do {$dInput = $startPath.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost=$true}))} While ($dInput -eq "Cancel") #2. 窗口小, TopMost开
    #由于选择根目录时路径变量含"\", 而文件夹时路径变量缺"\", 所以要自动判断并补上
    if (($startPath.SelectedPath.SubString($startPath.SelectedPath.Length-1) -eq "\") -eq $false) {$startPath.SelectedPath+="\"}
    return $startPath.SelectedPath
}
Function Read-MultiLineInputDialog([string]$Message, [string]$WindowTitle, [string]$DefaultText) {#「@Daniel Schroeder」多行文本输入框
    $DebugPreference = 'Continue'
    if (($host.name -match 'consolehost')) { #PowerShell窗口
        Write-Debug "√ 运行于PowerShell Console窗口中,使用GWMI分辨率信息"
        $oWidth  = gwmi win32_videocontroller | select-object CurrentHorizontalResolution -first 1
        $oHeight = gwmi win32_videocontroller | select-object CurrentVerticalResolution -first 1
        [int]$mWidth  = [Convert]::ToInt32($oWidth.CurrentHorizontalResolution)
        [int]$mHeight = [Convert]::ToInt32($oHeight.CurrentVerticalResolution)
        #Write-Debug "√ $mWidth x $mHeight"
    }
    else { #PowerShell ISE窗口
        Write-Debug "√ 运行于PowerShell ISE窗口中,使用系统分辨率信息"
        [int]$mWidth  = [System.Windows.Forms.SystemInformation]::PrimaryMonitorSize.Width
        [int]$mHeight = [System.Windows.Forms.SystemInformation]::PrimaryMonitorSize.Height
    }
    Add-Type -AssemblyName System.Drawing
    Add-Type -AssemblyName System.Windows.Forms
    #根据显示器大小换算窗口内说明文本起始位置和大小
    [int]$LBStartX = [math]::Round($mWidth /192)
    [int]$LBStartY = [math]::Round($mHeight/108)
    [int]$LblSizeX = [math]::Round($mWidth /19)
    [int]$LblSizeY = [math]::Round($mHeight/54)
    # 窗口内说明文本
    $label = New-Object System.Windows.Forms.Label
    $label.Location = New-Object System.Drawing.Size($LBStartX,$LBStartY) #非换算
    $label.Size = New-Object System.Drawing.Size($LblSizeX,$LblSizeY) #非换算,$Message太长则增加
    $label.AutoSize = $true
    $label.Text = $Message
    #根据显示器大小换算文本框起始位置和大小
    [int]$TBStartX = [math]::Round($mWidth /192)
    [int]$TBStartY = [math]::Round($mHeight/27)
    [int]$TblSizeX = [math]::Round($mWidth /3.73)
    [int]$TblSizeY = [math]::Round($mHeight/2.57)
    #绘制文本框
    $textBox = New-Object System.Windows.Forms.TextBox
    $textBox.Location = New-Object System.Drawing.Size($TBStartX,$TBStartY) #文本框左上起始位,非换算
    $textBox.Size = New-Object System.Drawing.Size($TblSizeX,$TblSizeY) #文本框大小,换算X-Y
    $textBox.Font = New-Object System.Drawing.Font((New-Object System.Windows.Forms.Form).font.Name,10) #修复高DPI下字号过小的"bug"
    $textBox.AcceptsReturn = $true
    $textBox.AcceptsTab = $false
    $textBox.Multiline = $true
    $textBox.ScrollBars = 'Both'
    $textBox.Text = $DefaultText
    #根据显示器大小换算OK按钮起始位置和大小
    [int]$OKStartX = [math]::Round($mWidth /4.7)
    [int]$OKStartY = [math]::Round($mHeight/108)
    [int]$OKbSizeX = [math]::Round($mWidth /34.92)
    [int]$OKbSizeY = [math]::Round($mHeight/47)
    #绘制OK按钮
    $okButton = New-Object System.Windows.Forms.Button
    $okButton.Location = New-Object System.Drawing.Size($OKStartX,$OKStartY) #OK按钮左上起始位,换算
    $okButton.Size = New-Object System.Drawing.Size($OKbSizeX,$OKbSizeY) #OK按钮大小,换算
    $okButton.Text = "确认"
    $okButton.DialogResult = "OK"
    $okButton.Add_Click({$form.Tag = $textBox.Text; $form.Close()})
    #根据显示器大小换算Cancel按钮起始位置
    [int]$ClStartX = [math]::Round($mWidth /4.08)
    [int]$ClStartY = $OKStartY
    [int]$ClbSizeX = $OKbSizeX
    [int]$ClbSizeY = $OKbSizeY
    #绘制Cancel按钮
    $cancelButton = New-Object System.Windows.Forms.Button
    $cancelButton.Location = New-Object System.Drawing.Size($ClStartX,$ClStartY)
    $cancelButton.Size = New-Object System.Drawing.Size($ClbSizeX,$ClbSizeY)
    $cancelButton.Text = "取消"
    $cancelButton.DialogResult = "Cancel"
    $cancelButton.Add_Click({$form.Tag = $null; $form.Close()})
    #根据显示器分辨率换算窗口大小
    [int]$formSizeX = [math]::Round($mWidth /3.56)
    [int]$formSizeY = [math]::Round($mHeight/2.18)
    #绘制窗口
    $form = New-Object System.Windows.Forms.Form
    $form.Text = $WindowTitle
    $form.Size = New-Object System.Drawing.Size($formSizeX,$formSizeY) #完整窗口大小,换算
    $form.FormBorderStyle = 'FixedSingle'
    $form.StartPosition = "CenterScreen"
    $form.AutoSizeMode = 'GrowAndShrink'
    $form.Topmost = $True
    $form.AcceptButton = $okButton
    $form.CancelButton = $cancelButton
    $form.ShowInTaskbar = $true
    #将控制元素添加到绘制的窗口
    $form.Controls.Add($label); $form.Controls.Add($textBox); $form.Controls.Add($okButton); $form.Controls.Add($cancelButton)
    #初始化并显示窗口
    $form.Add_Shown({$form.Activate()})
    #用Do-While循环拦截误操作(取消/关闭选择窗)
    Do {$dInput = $form.ShowDialog(); if ($dInput -eq "Cancel") {Write-Debug "× 不可取消,请关闭PowerShell窗口"}} While ($dInput -eq "Cancel")
    #回执输入内容
    return $form.Tag
}
#「@MrNetTek」高DPI显示渲染模式的System.Windows.Forms
Add-Type -TypeDefinition @'
using System.Runtime.InteropServices;
public class ProcessDPI {
    [DllImport("user32.dll", SetLastError=true)]
    public static extern bool SetProcessDPIAware();      
}
'@
$null = [ProcessDPI]::SetProcessDPIAware()

clear
#准备一个记事本窗口,将所有要下载的视频链接粘贴进去

$link=""
Do {($link = Read-MultiLineInputDialog -Message "★ 粘贴所有要下载的视频链接,多个链接通过换行区分" -WindowTitle "★ yt-dlp-ops 下载链接输入窗口" -DefaultText ""); if ($link -eq "") {Write-Error "× 输入了空值,重试"}} While ($link -eq "")

Do {Read-Host "`r`n将打开[选择视频下载路径]的选择窗,按Enter继续"; $DownloadPath = whichlocation; $DownloadPath #下载视频路径
    Read-Host "`r`n将打开[选择yt-dlp.exe]的选择窗,按Enter继续";   $ytdlpAppPath = whereisit;     $ytdlpAppPath #下载器路径
    Read-Host "`r`n将打开[导出批处理路径]的选择窗,按Enter继续";   $CmdPrintPath = whichlocation; $CmdPrintPath #导出批处理路径
    ""
    if (Test-Path $DownloadPath) {Write-Host "√ yt-dlp.exe路径正常"} else {Write-Error "× yt-dlp.exe路径异常"; pause; exit}
    if (Test-Path $ytdlpAppPath) {Write-Host "√ 视频文件夹路径正常"} else {Write-Error "× 视频文件夹路径异常"; pause; exit}
    if (Test-Path $CmdPrintPath) {Write-Host "√ 导出批处理路径正常"} else {Write-Error "× 导出批处理路径异常"; pause; exit}
    ""
} While ((Test-Path $ytdlpAppPath) + (Test-Path $DownloadPath) + (Test-Path $CmdPrintPath) -ne 3) #$true+$true+$true=3

$CookieIOPath = $DownloadPath+"ytdlpCookies.txt" #导出和后来导入Cookie用的txt

$brwsrSelect=$cookieExp=""
Switch (Read-Host "输入y以添加一行[导出当前浏览器cookie]的命令,或按Enter跳过") {
    y {
        $brwsrSelect = Switch (Read-Host "选择含登录Cookie的浏览器 [A: Chrome | B: Brave | C: Chromium | D: Opera | E: Edge | F: Firefox | G: Safari | H: Vivaldi]") {
        a {"Chrome"} b {"Brave"} c {"Chromium"} d {"Opera"} e {"Edge"} f {"Firefox"} g {"Safari"} h {"Vivaldi"} default {"Chromium"}
    }
    $cookieExp = "$ytdlpAppPath --ignore-errors --cookies-from-browser $brwsrSelect --cookies $CookieIOPath"
    Write-Output "!仅导出Cookies的命令会导致yt-dlp弹出没有视频下载链接的报错,无视即可`n"
    }
    Default {"√ 跳过"}
}

$cookieImp = "--cookies $CookieIOPath" #此处完成yt-dlp导出以及导入cookies的命令
$CmdPrint = $CmdPrintPath+"yt-dlp-download.bat"; "`r`n将导出批处理文件:$CmdPrint" #此处完成批处理导出路径的搭建
if (Test-Path $CmdPrint) {Remove-Item $CmdPrint} #由于写入使用了 >> 所以需要通过检测删除机制覆盖原始文件

$ctrl_gen = @()
$ctrl_gen += "
chcp 65001

@echo off"
if ($brwsrSelect -ne "") {
    $ctrl_gen+="
@ECHO 从浏览器生成cookies到$CookieIOPath"
    $ctrl_gen+=$cookieExp
} #将导出cookie的命令写入控制批处理

$ctrl_gen += "
@ECHO 下载视频与随机间隔"
$link.Split("`r`n").Trim() | where {$_ -ne ""}  | ForEach-Object {
    $ctrl_gen+="timeout /nobreak /t "+(Get-Random -InputObject 1,2,3,4,5,6,7).ToString() #每个下载随机间隔1~7秒以降低对视频平台的压力,从而实现对普通观看过程的模拟,并降低被拦截的概率
    $ctrl_gen+="$ytdlpAppPath --newline --ignore-errors -o `"$DownloadPath%%(title)s.%%(ext)s`" --ignore-config --hls-prefer-native --no-playlist $_ $cookieImp " #批处理中需要%%而不是常规%(title)
}

$ctrl_gen += "
pause
"
$utf8NoBOM=New-Object System.Text.UTF8Encoding $false #导出utf-8NoBOM文本编码用
[IO.File]::WriteAllLines($CmdPrint, $ctrl_gen, $utf8NoBOM) #强制导出utf-8NoBOM编码
Write-Output "`r`n完成`r`n"

pause

工作以及使用流程

  1. 下载一个yt-dlp.exe工具
    • 最好创建一个文件夹用于放置
  2. 打开一个记事本/空白文本文档进程
  3. 将所有要下载的视频链接(取决于yt-dlp支持的视频网站)粘贴进去
    • 最好去掉如&list&si之类的后缀,以防止油管未来在链接中用这种方式偷偷添加标记(可能想多了)
    • 可以用批量拷贝链接的浏览器插件,如Snap Links Plus
  4. 创建一个文件夹用于下载所有视频
  5. 运行yt-dlp-ops.ps1,可以在PowerShell ISE中打开并运行,也可以右键→用PowerShell运行
    • 将所有要下载的视频链接粘贴进去
    • 通过弹出的选择窗选择下载视频的路径
    • 通过弹出的选择窗定位yt-dlp.exe
    • 通过弹出的选择窗选择导出批处理的路径
    • 每天第一次使用时,选择y以添加一行用于导出当前浏览器cookie的命令
      • 用于以你的账号登录油管或其它视频网站,从而确保下载进程正常
    • 完成!双击运行导出的yt-dlp-download.bat以开始下载

其它特性

  • 每个下载线程之间会随机生成一个1~7秒的间隔,从而让下载进程更像正常观看,且每次都不会重复
  • 通过GUI窗口选择各种路径,极大地降低了操作难度
  • 导出UTF-8 No BOM编码的批处理

那么就这样~

打赏信息

这玩意不一定适合打赏

  • 更新2:添加了高DPI的Windows Form GUI选择窗渲染,添加了运行后暂停
  • 更新3:添加了极为先进的分辨率自适应多行文本输入框Windows Form GUI,用于改进输入并编辑多链接的流程
  • 更新4:添加了区分PowerShell ISE和Console窗口的区分,以正确统计分辨率
  • 更新5:通过文本框Font属性搭配加载默认Windows Form并获取其中属性的方式实现了高DPI文本框字号过小的修复