clippy.ps1

<#PSScriptInfo
 
.VERSION 1.0
 
.GUID dce65b3a-d917-425b-9090-d82b368e12fa
 
.AUTHOR Read Stanton (Decimation)
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS Downloader yt-dlp ffmpeg clip
 
.LICENSEURI
 
.PROJECTURI https://github.com/Decimation/PowerShell-Library
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
.DESCRIPTION
Downloads clips from sites supported by yt-dlp
 
.PRIVATEDATA
 
#>



<#
.DESCRIPTION
Downloads a YouTube clip with yt-dlp and ffmpeg
.AUTHOR
.LINK
https://github.com/Decimation/PowerShell-Library
.EXAMPLE
& "yt-clip.ps1" -Url "https://www.youtube.com/watch?v=SNgNBsCI4EA" -Start '1:10' -End "1:26"
.EXAMPLE
& "yt-clip.ps1" -Url "https://youtu.be/YPqYvll6XD0" -Start '10:52' -End "11:38" -Args2 @('-preset','veryfast')
.EXAMPLE
& "yt-clip.ps1" -u "https://youtu.be/YPqYvll6XD0" -s '1:00' -e "2:00" -Args2 @('-preset','veryfast')
#>

param (
    # Url
    [Parameter(Mandatory)]
    [alias('u')]
    $Url, 
    # Start time
    [Parameter(Mandatory)]
    [Alias('s')]
    $Start = '0:0:0', 
    # End time
    [Parameter(Mandatory)]
    [alias('e')]
    $End,
    [Parameter(Mandatory = $false)]
    $Output = $null,
    # yt-dlp args
    [Parameter(Mandatory = $false)]
    $Args1,
    # ffmpeg args
    [Parameter(Mandatory = $false)]
    $Args2 = @('-y'),
    [Alias('cf')][switch]$Confirm
)

#$ErrorActionPreference = 'Abort'

function script:Get-ParsedTime($t) {
    $st = $t.Split(':')

    switch ($st.Length) {
        1 {
            $t = [TimeSpan]::FromSeconds($t)
        }
        2 {
            $t = "0:" + $t
        }
        default {
        }
    }
    
    
    try {
        $t = [timespan]::ParseExact($t, "g", [cultureinfo]::CurrentCulture)    
    }
    catch {
        $t = ([timespan]::Parse($t))
    }

    return $t
}

$Start = script:Get-ParsedTime($Start)
$End = script:Get-ParsedTime($End)

Write-Host "Start: $Start" -ForegroundColor 'Cyan'
Write-Host "End: $End" -ForegroundColor 'Cyan'

$e_ffmpeg = 'ffmpeg'
$e_ytdlp = 'yt-dlp'

$c_ytdlp = (Get-Command $e_ytdlp)
$c_ffmpeg = (Get-Command $e_ffmpeg)

if (-not $c_ytdlp) {
    Write-Error "$e_ytdlp not found"
    return
}

if (-not $c_ffmpeg) {
    Write-Error "$e_ffmpeg not found"
    return
}

Write-Host "$($c_ytdlp.Path)" -ForegroundColor 'DarkGray'
Write-Host "$($c_ffmpeg.Path)" -ForegroundColor 'DarkGray'

# $tf = "hh\.mm\.ss"
$tf = "hh\hmm\mss\s"

$fs = $Start.ToString($tf)
$fe = $End.ToString($tf)

# $arg2 = @($Url, '--print', 'id')
$arg2 = @($Url, '--print', 'title')

$il = [System.IO.Path]::GetInvalidFileNameChars()

$Output ??= "$(yt-dlp @arg2) ($fs - $fe).mp4" -replace '//'
Write-Host "Output filename: $Output" -ForegroundColor 'Green'

if (Test-Path $Output) {
    $yn = Read-Host -Prompt "$Output already exists. Remove? [y/n/a]"
    switch ($yn) {
        'y' {
            Remove-Item $Output
        }
        'n' {
            
        }
        'a' {
            return
        }
    }
}


function script:Read-Confirmation {
    
    if ($Confirm) {
        switch (Read-Host "Continue? [y/n] (or press enter)") {
            default {
                
            }
            'y' {
                
            }
            'n' {
                exit
            }
        }
    }

}

script:Read-Confirmation

# yt-dlp args

$arg1 = @('-g', $Args1, '--youtube-skip-dash-manifest', $Url)
$x1 = yt-dlp @arg1

$video = $x1[0]
$audio = $x1[1]
$duration = $End - $Start

$ffArgs = @('-ss', $Start, '-i', $video)

if ($audio) {
    $ffArgs += @('-ss', $Start, '-i', $audio)
}

$ffArgs += @('-t', $duration, `
        "-map", "0:v", "-map", "1:a", `
        "-c:v", "libx264", "-c:a", "aac") `
    + $Args2 + "`"$Output`""


Write-Host "Duration: ($duration)" -ForegroundColor 'DarkGray'
Write-Debug "$($ffArgs -join ' ')"

script:Read-Confirmation

ffmpeg @ffArgs
$of = $(Resolve-Path $Output)

Write-Host "Output: $of" -ForegroundColor 'Green'

<# $p = Start-Process -FilePath 'ffmpeg.exe' -RedirectStandardOutput:$true `
    -ArgumentList $ffArgs -NoNewWindow -PassThru
 
return $p #>