Cover .ts to mp4 File Name:convert.ps1 and run in powershell ./convert.ps1 Requirement: HandBrakeCLI.exe and Main Folder LLB inside llb search all .ts file convert into MP4 # convert.ps1 — speed-optimized HandBrake batch converter $hb = Join-Path $PSScriptRoot 'HandBrakeCLI.exe' $root = Join-Path $PSScriptRoot 'llb' if (-not (Test-Path $hb)) { Write-Host "❌ HandBrakeCLI.exe not found at $hb"; exit 1 } if (-not (Test-Path $root)) { Write-Host "❌ Folder not found: $root"; exit 1 } # ---- Speed options you can tweak ---- $Quality = 22 # lower = better quality, bigger file (18–28 typical) $PresetCPU = 'veryfast' # x264 preset for speed $PresetGPU = 'fast' # GPU preset; 'faster'/'veryfast' exist but 'fast' is good balance $PreferHardware = $true # try GPU encoders first for max speed $MaxConcurrent = 1 # keep 1 for GPU (best), raise to 2–4 if CPU only (see NOTE below) # ---- Detect available encoders (GPU first) ---- $encodersOut = (& $hb --encoders) -join "`n" $Encoder = 'x264' if ($PreferHardware) { if ($encodersOut -match 'nvenc_h264') { $Encoder = 'nvenc_h264' } elseif ($encodersOut -match 'qsv_h264') { $Encoder = 'qsv_h264' } elseif ($encodersOut -match 'amf_h264' -or $encodersOut -match 'vce_h264') { $Encoder = 'amf_h264' } } $Preset = if ($Encoder -eq 'x264') { $PresetCPU } else { $PresetGPU } Write-Host "================= HandBrake Speed Mode =================" Write-Host "Root: $root" Write-Host "Encoder: $Encoder" Write-Host "Preset: $Preset" Write-Host "Quality: $Quality" Write-Host "Concurrency: $MaxConcurrent" Write-Host "========================================================" # Common args to reduce scanning time and make MP4 web-optimized $Common = @( '--format','av_mp4', '--optimize', # fast-start '--subtitle','none', # skip subtitle scan '--crop','0:0:0:0', # no auto-crop scan '--previews','0', # skip preview analysis '--quality', "$Quality", '--encoder', "$Encoder", '--encoder-preset', "$Preset" ) # Helper to run one conversion (audio copy, then fallback to AAC if needed) function Convert-One { param([string]$tsPath) $mp4 = [System.IO.Path]::ChangeExtension($tsPath, '.mp4') if (Test-Path $mp4) { Write-Host "⏭️ Skipping (exists): $mp4" return } Write-Host "--------------------------------------------------" Write-Host "Converting: $tsPath" $sw = [Diagnostics.Stopwatch]::StartNew() $args1 = @('-i', $tsPath, '-o', $mp4) + $Common + @('--aencoder','copy') & $hb @args1 $code = $LASTEXITCODE if ($code -ne 0) { Write-Host "⚠️ Audio copy failed (exit $code), retrying with AAC encode..." $args2 = @('-i', $tsPath, '-o', $mp4) + $Common + @('--aencoder','av_aac') & $hb @args2 $code = $LASTEXITCODE } $sw.Stop() if ($code -eq 0) { Write-Host ("✅ Done: {0} ({1:n1} sec)" -f $tsPath, $sw.Elapsed.TotalSeconds) } else { Write-Host "❌ Failed: $tsPath (exit $code)" if (Test-Path $mp4) { Remove-Item $mp4 -Force -ErrorAction SilentlyContinue } # clean partial } } # Gather files $files = Get-ChildItem -Path $root -Recurse -Filter '*.ts' | Select-Object -ExpandProperty FullName if ($files.Count -eq 0) { Write-Host "No .ts files found under $root" exit 0 } # ---- SIMPLE SEQUENTIAL (safe & reliable, preserves your AAC fallback) ---- if ($MaxConcurrent -le 1) { foreach ($ts in $files) { Convert-One -tsPath $ts } } else { # ---- OPTIONAL PARALLEL MODE (PowerShell 5.1+ with background jobs) ---- # NOTE: Use this mainly for CPU encoding. For GPU encoders, 1 job is usually fastest & most stable. $jobs = @() foreach ($ts in $files) { while ( ($jobs | Where-Object { $_.State -eq 'Running' }).Count -ge $MaxConcurrent ) { Start-Sleep -Seconds 1 # Cleanup completed jobs to keep the list small $jobs = $jobs | Where-Object { $_.State -eq 'Running' -or $_.State -eq 'NotStarted' } } $jobs += Start-Job -ScriptBlock { param($hbPath, $tsItem, $commonArgs) function Conv { param($hbP, $tsP, $com) $mp4 = [System.IO.Path]::ChangeExtension($tsP, '.mp4') if (Test-Path $mp4) { return } & $hbP @('-i', $tsP, '-o', $mp4) + $com + @('--aencoder','copy') if ($LASTEXITCODE -ne 0) { & $hbP @('-i', $tsP, '-o', $mp4) + $com + @('--aencoder','av_aac') } } Conv -hbP $hbPath -tsP $tsItem -com $commonArgs } -ArgumentList $hb, $ts, $Common } Write-Host "🔄 Waiting for $($jobs.Count) job(s) to finish..." Wait-Job -Job $jobs | Out-Null Receive-Job -Job $jobs | Out-Null Remove-Job -Job $jobs | Out-Null } Write-Host "========================================================" Write-Host "✅ All .ts files processed."