bilibili mp3 下载脚本

下载脚本

bbdowner.ps1

# 从 bilibili 下载视频并转为 mp3
# 调用了 bbdown 命令, dotnet tool install -g bbdown
# 调用了 ffmpeg 命令, brew install ffmpeg

$ErrorActionPreference = 'stop'

# 兼容一些低版本的 .net 工具
$env:DOTNET_ROLL_FORWARD = "LatestMajor"

class BbDownloader {

    # 解析传入的链接文本, 获取链接并下载视频到桌面
    [void] DownloadFromContentToDesktop([string]$urlContent) { 

        [string[]]$urls = $urlContent -split "[\r\n]" | 
        ForEach-Object { $this.FormatUrl($_) } | 
        Where-Object { $_.Length -gt 0 } 

        # $urls 去重
        $urls = $urls | Select-Object -Unique

        $this.DownloadToDesktop($urls)
    }

    # 格式化链接
    [string] FormatUrl([string]$url) { 

        # 正则取 https://www.bilibili.com/video/ 开头到空格为止
        $matche = [Regex]::Match($url, "https://www.bilibili.com/video/[^ ]+")   
        if (-not $matche.Success) {
            return ""
        }

        $url = $matche.Value

        # url 去掉 ?后的部分
        [System.UriBuilder]$uri = [System.UriBuilder]::new($url)
        $uri.Query = ""

        $url = "$($uri.Uri)"

        return $url
    }

    # 下载视频到桌面
    [void] DownloadToDesktop([string[]]$urls) { 

        $desktop = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Desktop)
        $mp3Dir = Join-Path $desktop -ChildPath "mp3"

        $this.Download($urls, $mp3Dir)
    }

    [void] Download([string[]]$urls, [string]$outputDir) { 

        foreach ($url in $urls) { 

            Write-Host "处理链接 $url"

            $tmpDir = Join-Path $([System.IO.Path]::GetTempPath()) -ChildPath $([System.IO.Path]::GetRandomFileName())
            Write-Host "临时目录 $tmpDir"

            New-Item -Path $tmpDir -ItemType Directory | Out-Null

            $current = Get-Location
            Set-Location $tmpDir

            try {

                $videoFile = $this.DownloadVideoFromUrl($url)

                $mp3File = $this.ConvertVideoToMp3($videoFile)

                $this.MoveFileToOutputDir($mp3File, $outputDir)
            }
            finally {

                Set-Location $current

                Remove-Item -Path $tmpDir -Recurse -Force                
                # Start-Process 'open' $tmpDir
            }

        }

        Start-Process "open" -ArgumentList $outputDir

        Write-Host "处理完成"
    }

    # 移动文件到输出目录
    [void] MoveFileToOutputDir([string]$file, [string]$outputDir) {

        New-Item -Path $outputDir -ItemType Directory -Force | Out-Null

        $fileName = [System.IO.Path]::GetFileName($file)
        $outputFile = Join-Path $outputDir -ChildPath $fileName

        Move-Item $file -Destination $outputFile -Force
    }

    # 使用 ffmpeg 转换视频
    [string] ConvertVideoToMp3([string]$videoFile) {

        $mp3File = [System.IO.Path]::ChangeExtension($videoFile, ".mp3")

        # 调用 ffmpeg 转换视频
        $command = "ffmpeg -i '$videoFile' -vn -acodec libmp3lame -qscale:a 0 -ac 2 '$mp3File' -y 2>&1"
        Write-Host $command
        Invoke-Expression $command | Out-Host

        if ($LASTEXITCODE -ne 0) {
            throw "FFmpeg 转换失败!"
        }

        return $mp3File
    }

    [string] GetSafeFileName([string]$fileName, [string]$replacement = "_") {

        # 1. 获取非法字符
        $invalidChars = [System.IO.Path]::GetInvalidFileNameChars()
        $pattern = "[{0}]" -f [regex]::Escape(-join $invalidChars)

        # 2. 替换非法字符
        $result = $fileName -replace $pattern, $replacement

        # 3. 移除不可见控制字符并修剪两端
        $result = $result -replace "[\x00-\x1f]", ""

        return $result.Trim()
    }

    [string] GetDownloadMp4File() {

        $filePath = Get-ChildItem -Filter *.mp4 | Select-Object -First 1 | ForEach-Object { $_.FullName }

        if ($filePath -and $(Test-Path $filePath)) {
            return $filePath

        }

        Write-Host "可能有多首音乐"

        # 找到第一个 mp4 文件认为是需要的音乐
        $mp4File = Get-ChildItem -Filter *.mp4 -Recurse | Select-Object -First 1 | ForEach-Object { $_.FullName }

        if (-not $mp4File -and $(Test-Path $mp4File)) {
            throw "没有找到视频文件"
        }            

        # 找到第一个文件夹认为是需要的音乐文件名
        $dirPath = Get-ChildItem -Directory | Select-Object -First 1 # | ForEach-Object { $_.Name }
        if (-not $dirPath -and $(Test-Path $dirPath)) {
            throw "没有找到视频文件所在目录"
        }

        $dirName = [System.IO.Path]::GetFileName($dirPath)
        $dirRoot = [System.IO.Path]::GetDirectoryName($dirPath)
        $fileName = $this.GetSafeFileName($dirName, "-")
        $ext = [System.IO.Path]::GetExtension($mp4File)

        $filePath = Join-Path $dirRoot -ChildPath "$($fileName)$($ext)"

        [System.IO.File]::Copy($mp4File, $filePath)

        # Copy-Item $mp4File -Destination $filePath
        # Move-Item $mp4File -Destination $filePath

        if (-not $? -or (-not $filePath) -or (-not (Test-Path $filePath))) {
            throw "复制视频文件失败"
        }

        return $filePath
    }

    # 使用 bbdown 下载视频, 并得到 视频文件
    [string] DownloadVideoFromUrl([string]$url) {        

        BBDown $url 2>&1 | Out-Host 

        if ($LASTEXITCODE -ne 0) {
            throw "bbdown 命令执行失败, 错误码 $LASTEXITCODE"
        }

        return $this.GetDownloadMp4File()

    }
}

# 一行一个下载链接
$urls = "      
22  Attr    href    https://www.bilibili.com/video/BV1vrCdYvEQb?spm_id_from=333.1387.favlist.content.click      
23  Attr    href    https://www.bilibili.com/video/BV1vrCdYvEQb?spm_id_from=333.1387.favlist.content.click      

"

$bbDownloader = [BbDownloader]::new()
$bbDownloader.DownloadFromContentToDesktop($urls)
上一篇
下一篇