@@ -0,0 +1,326 @@+<# :
+@echo off
+powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))"
+exit /b %errorlevel%
+#>
+
+# ============================================================
+# Converter Imagens - redimensiona/converte em lote (ImageMagick)
+# Arquivo único .cmd (auto-executável como PowerShell)
+# ============================================================
+
+try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Converter Imagens' } catch {}
+
+function Pausar {
+ Write-Host ''
+ Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray
+ [void][System.Console]::ReadLine()
+}
+
+function Titulo($texto) {
+ Write-Host ''
+ Write-Host '============================================================' -ForegroundColor DarkCyan
+ Write-Host " $texto" -ForegroundColor Cyan
+ Write-Host '============================================================' -ForegroundColor DarkCyan
+}
+
+# ------------------------------------------------------------
+# Configuração persistente (lembra últimas escolhas)
+# ------------------------------------------------------------
+$configDir = Join-Path $env:APPDATA 'ConverterImagens'
+$configFile = Join-Path $configDir 'config.json'
+
+function Carregar-Config {
+ if (Test-Path -LiteralPath $configFile) {
+ try { return Get-Content -LiteralPath $configFile -Raw -Encoding UTF8 | ConvertFrom-Json } catch {}
+ }
+ return $null
+}
+
+function Salvar-Config($entrada, $formato, $modo, $valor) {
+ try {
+ New-Item -ItemType Directory -Path $configDir -Force | Out-Null
+ [pscustomobject]@{
+ UltimaEntrada = $entrada
+ UltimoFormato = $formato
+ UltimoModo = $modo
+ UltimoValor = $valor
+ } | ConvertTo-Json | Set-Content -LiteralPath $configFile -Encoding UTF8
+ } catch {}
+}
+
+# Formatos de imagem reconhecidos na entrada
+$extensoes = @(
+ '.jpg', '.jpeg', '.png', '.webp', '.bmp',
+ '.gif', '.tif', '.tiff', '.heic', '.heif', '.avif'
+)
+
+# Formatos de saída oferecidos
+$formatos = @(
+ [pscustomobject]@{ Id=1; Nome='Manter formato original'; Desc='Só redimensiona, mantém o tipo do arquivo'; Ext='' },
+ [pscustomobject]@{ Id=2; Nome='JPG'; Desc='Fotos, menor tamanho (sem transparência)'; Ext='.jpg' },
+ [pscustomobject]@{ Id=3; Nome='PNG'; Desc='Sem perdas, mantém transparência'; Ext='.png' },
+ [pscustomobject]@{ Id=4; Nome='WebP'; Desc='Ótima compressão, qualidade alta'; Ext='.webp' }
+)
+
+# ------------------------------------------------------------
+# 1) Verificação de dependências
+# ------------------------------------------------------------
+Titulo 'Converter Imagens'
+Write-Host 'Verificando o que é necessário...' -ForegroundColor Gray
+
+$magick = Get-Command magick -ErrorAction SilentlyContinue
+if (-not $magick) {
+ Write-Host ''
+ Write-Host '[ERRO] O ImageMagick não foi encontrado no sistema.' -ForegroundColor Red
+ Write-Host 'Ele é necessário para converter as imagens.' -ForegroundColor Yellow
+ Write-Host ''
+ Write-Host 'Para instalar, abra o Terminal / Prompt e rode:' -ForegroundColor Gray
+ Write-Host ' winget install ImageMagick.ImageMagick' -ForegroundColor White
+ Write-Host ''
+ Write-Host 'Depois feche e abra este programa novamente.' -ForegroundColor Gray
+ Pausar
+ exit 1
+}
+Write-Host "[OK] ImageMagick encontrado: $($magick.Source)" -ForegroundColor Green
+
+$config = Carregar-Config
+
+# ------------------------------------------------------------
+# 2) Escolha da pasta OU de uma imagem única
+# ------------------------------------------------------------
+$entradaPadrao = Join-Path $env:USERPROFILE 'Downloads\Imagens'
+$entradaAtual = if ($config -and $config.UltimaEntrada) { $config.UltimaEntrada } else { $entradaPadrao }
+
+$modoArquivo = $false
+$arquivoUnico = $null
+
+while ($true) {
+ Titulo 'Pasta ou imagem'
+ Write-Host 'Caminho atual:' -ForegroundColor Gray
+ Write-Host " $entradaAtual" -ForegroundColor White
+ Write-Host ''
+ Write-Host 'Enter = confirmar | ou cole/digite uma PASTA ou uma IMAGEM' -ForegroundColor DarkGray
+ $resp = Read-Host 'Pasta/imagem'
+
+ if (-not [string]::IsNullOrWhiteSpace($resp)) {
+ $entradaAtual = $resp.Trim().Trim('"')
+ }
+
+ if (Test-Path -LiteralPath $entradaAtual -PathType Leaf) {
+ $ext = [System.IO.Path]::GetExtension($entradaAtual).ToLowerInvariant()
+ if ($extensoes -notcontains $ext) {
+ Write-Host ''
+ Write-Host "[!] Esse arquivo não é uma imagem suportada ($ext)." -ForegroundColor Yellow
+ continue
+ }
+ $modoArquivo = $true
+ $arquivoUnico = (Resolve-Path -LiteralPath $entradaAtual).Path
+ break
+ }
+ elseif (Test-Path -LiteralPath $entradaAtual -PathType Container) {
+ $modoArquivo = $false
+ break
+ }
+
+ Write-Host ''
+ Write-Host "[!] Caminho não encontrado: $entradaAtual" -ForegroundColor Yellow
+ $criar = Read-Host 'Criar como pasta? (s/N)'
+ if ($criar -match '^(s|sim|y|yes)$') {
+ try {
+ New-Item -ItemType Directory -Path $entradaAtual -Force | Out-Null
+ Write-Host "[OK] Pasta criada." -ForegroundColor Green
+ $modoArquivo = $false
+ break
+ } catch {
+ Write-Host "[ERRO] Não foi possível criar a pasta." -ForegroundColor Red
+ }
+ }
+}
+
+if ($modoArquivo) {
+ $arquivoObj = Get-Item -LiteralPath $arquivoUnico
+ $origem = $arquivoObj.DirectoryName
+} else {
+ $origem = (Resolve-Path -LiteralPath $entradaAtual).Path
+}
+$destino = Join-Path $origem 'Convertido'
+
+# ------------------------------------------------------------
+# 3) Formato de saída
+# ------------------------------------------------------------
+$formatoPadrao = if ($config -and $config.UltimoFormato) { [int]$config.UltimoFormato } else { 1 }
+
+Titulo 'Formato de saída'
+foreach ($fmt in $formatos) {
+ $marca = if ($fmt.Id -eq $formatoPadrao) { '>' } else { ' ' }
+ $cor = if ($fmt.Id -eq $formatoPadrao) { 'White' } else { 'Gray' }
+ Write-Host ("{0} [{1}] {2}" -f $marca, $fmt.Id, $fmt.Nome) -ForegroundColor $cor
+ Write-Host (" {0}" -f $fmt.Desc) -ForegroundColor DarkGray
+}
+Write-Host ''
+Write-Host "Enter = usar a opção padrão [$formatoPadrao]" -ForegroundColor DarkGray
+
+$formato = $null
+while (-not $formato) {
+ $escolha = Read-Host 'Formato (1-4)'
+ if ([string]::IsNullOrWhiteSpace($escolha)) { $escolha = "$formatoPadrao" }
+ $formato = $formatos | Where-Object { $_.Id -eq ($escolha -as [int]) } | Select-Object -First 1
+ if (-not $formato) { Write-Host '[!] Escolha inválida. Digite um número de 1 a 4.' -ForegroundColor Yellow }
+}
+
+# ------------------------------------------------------------
+# 4) Tamanho: px (maior lado), % ou não redimensionar
+# ------------------------------------------------------------
+$modoPadrao = if ($config -and $config.UltimoModo) { $config.UltimoModo } else { 'px' }
+
+Titulo 'Tamanho'
+Write-Host ' [1] Pixels do maior lado (ex.: 1920 - a imagem cabe em 1920x1920)' -ForegroundColor Gray
+Write-Host ' [2] Porcentagem (%) (ex.: 50 = metade do tamanho)' -ForegroundColor Gray
+Write-Host ' [3] Não redimensionar (só converter o formato)' -ForegroundColor Gray
+Write-Host ''
+$rotuloPadrao = switch ($modoPadrao) { 'px' {'1'} 'pct' {'2'} 'none' {'3'} default {'1'} }
+Write-Host "Enter = usar o modo padrão [$rotuloPadrao]" -ForegroundColor DarkGray
+
+$modo = $null
+while (-not $modo) {
+ $op = Read-Host 'Modo (1-3)'
+ if ([string]::IsNullOrWhiteSpace($op)) { $op = $rotuloPadrao }
+ switch ($op) {
+ '1' { $modo = 'px' }
+ '2' { $modo = 'pct' }
+ '3' { $modo = 'none' }
+ default { Write-Host '[!] Escolha inválida. Digite 1, 2 ou 3.' -ForegroundColor Yellow }
+ }
+}
+
+$valor = $null
+$resizeArg = $null
+$resumoTam = ''
+
+if ($modo -eq 'px') {
+ $valorPadrao = if ($config -and $config.UltimoModo -eq 'px' -and $config.UltimoValor) { $config.UltimoValor } else { '' }
+ while (-not $valor) {
+ $dica = if ($valorPadrao) { " (Enter = $valorPadrao)" } else { '' }
+ $ent = Read-Host "Maior lado em pixels$dica"
+ if ([string]::IsNullOrWhiteSpace($ent) -and $valorPadrao) { $ent = "$valorPadrao" }
+ $n = $ent -as [int]
+ if ($n -and $n -gt 0) { $valor = $n } else { Write-Host '[!] Digite um número de pixels válido (ex.: 1920).' -ForegroundColor Yellow }
+ }
+ # ">" garante proporcional e NUNCA amplia
+ $resizeArg = "$($valor)x$($valor)>"
+ $resumoTam = "maior lado $valor px"
+}
+elseif ($modo -eq 'pct') {
+ $valorPadrao = if ($config -and $config.UltimoModo -eq 'pct' -and $config.UltimoValor) { $config.UltimoValor } else { '' }
+ while (-not $valor) {
+ $dica = if ($valorPadrao) { " (Enter = $valorPadrao)" } else { '' }
+ $ent = Read-Host "Porcentagem %$dica"
+ if ([string]::IsNullOrWhiteSpace($ent) -and $valorPadrao) { $ent = "$valorPadrao" }
+ $p = ($ent -replace '%','') -as [double]
+ if ($p -and $p -gt 0) { $valor = $p } else { Write-Host '[!] Digite uma porcentagem válida (ex.: 50).' -ForegroundColor Yellow }
+ }
+ $resizeArg = "$valor%"
+ $resumoTam = "$valor%"
+}
+else {
+ $resumoTam = 'sem redimensionar'
+}
+
+Salvar-Config $origem $formato.Id $modo $valor
+
+# ------------------------------------------------------------
+# 5) Coleta das imagens
+# ------------------------------------------------------------
+if ($modoArquivo) {
+ $arquivos = @($arquivoObj)
+} else {
+ $arquivos = @(Get-ChildItem -LiteralPath $origem -Recurse -File -ErrorAction SilentlyContinue |
+ Where-Object {
+ ($extensoes -contains $_.Extension.ToLowerInvariant()) -and
+ (-not $_.FullName.StartsWith($destino, [System.StringComparison]::OrdinalIgnoreCase))
+ })
+}
+
+Titulo 'Processando'
+Write-Host "Pasta: $origem"
+Write-Host "Formato: $($formato.Nome)"
+Write-Host "Tamanho: $resumoTam"
+Write-Host "Destino: $destino"
+Write-Host "Imagens encontradas: $($arquivos.Count)" -ForegroundColor Cyan
+
+if ($arquivos.Count -eq 0) {
+ Write-Host ''
+ Write-Host 'Nenhuma imagem para converter nesse local.' -ForegroundColor Yellow
+ Pausar
+ exit 0
+}
+
+New-Item -ItemType Directory -Path $destino -Force | Out-Null
+
+$convertidos = 0
+$ignorados = 0
+$erros = 0
+$indice = 0
+
+foreach ($arquivo in $arquivos) {
+ $indice++
+
+ $relativo = $arquivo.FullName.Substring($origem.Length).TrimStart('\')
+ $subpasta = Split-Path -Path $relativo -Parent
+ $pastaSaida = if ([string]::IsNullOrWhiteSpace($subpasta)) { $destino } else { Join-Path $destino $subpasta }
+ New-Item -ItemType Directory -Path $pastaSaida -Force | Out-Null
+
+ # Extensão de saída: mantém a original ou usa a do formato escolhido
+ $extSaida = if ([string]::IsNullOrEmpty($formato.Ext)) { $arquivo.Extension.ToLowerInvariant() } else { $formato.Ext }
+ $saida = Join-Path $pastaSaida ($arquivo.BaseName + $extSaida)
+
+ Write-Host ''
+ Write-Host ("[{0}/{1}]" -f $indice, $arquivos.Count) -ForegroundColor Magenta -NoNewline
+ Write-Host " $($arquivo.Name)"
+
+ if (Test-Path -LiteralPath $saida) {
+ Write-Host " [IGNORADO] Já existe na pasta de destino." -ForegroundColor Yellow
+ $ignorados++
+ continue
+ }
+
+ # Entrada: para GIF/multiframe indo para formato de imagem única, pega o 1o quadro
+ $entradaMagick = $arquivo.FullName
+ if ($arquivo.Extension.ToLowerInvariant() -eq '.gif' -and $extSaida -ne '.gif') {
+ $entradaMagick = "$($arquivo.FullName)[0]"
+ }
+
+ $imgArgs = @($entradaMagick)
+ if ($resizeArg) { $imgArgs += @('-resize', $resizeArg) }
+ # JPG não tem transparência: achata sobre fundo branco
+ if ($extSaida -eq '.jpg' -or $extSaida -eq '.jpeg') {
+ $imgArgs += @('-background', 'white', '-alpha', 'remove', '-alpha', 'off')
+ }
+ if ($extSaida -in '.jpg', '.jpeg', '.webp') {
+ $imgArgs += @('-quality', '85')
+ }
+ $imgArgs += $saida
+
+ & magick @imgArgs
+
+ if ($LASTEXITCODE -eq 0 -and (Test-Path -LiteralPath $saida)) {
+ Write-Host " [OK] Convertida." -ForegroundColor Green
+ $convertidos++
+ } else {
+ Write-Host " [ERRO] Falha ao converter." -ForegroundColor Red
+ if (Test-Path -LiteralPath $saida) { Remove-Item -LiteralPath $saida -Force -ErrorAction SilentlyContinue }
+ $erros++
+ }
+}
+
+# ------------------------------------------------------------
+# 6) Resumo final
+# ------------------------------------------------------------
+Titulo 'Concluído'
+Write-Host "Convertidas: $convertidos" -ForegroundColor Green
+Write-Host "Ignoradas: $ignorados" -ForegroundColor Yellow
+Write-Host "Erros: $erros" -ForegroundColor $(if ($erros -gt 0) { 'Red' } else { 'Gray' })
+Write-Host "Destino: $destino"
+Pausar
@@ -0,0 +1,25 @@+# fast-scripts
+
+Scripts rápidos para Windows, em lote e com interface no terminal.
+
+
+
+
+
+## Programas
+
+### Converter Imagens
+
+`Imagens/converter-imagens.cmd`
+
+Redimensiona e converte imagens em lote com ImageMagick. Aceita uma pasta ou um arquivo, escolhe formato (JPG, PNG, WebP ou original) e tamanho (pixels, porcentagem ou só converter). A saída vai para a pasta `Convertido`.
+
+### Transcodificar
+
+`Vídeos/transcodificar.cmd`
+
+Converte vídeos em lote com FFmpeg para MP4 (H.264 + AAC). Aceita uma pasta ou um arquivo e oferece perfis de qualidade (da máxima à compacta). A saída vai para a pasta `Transcodificado`.
+
+---
+
+**Pablo Murad** — pablomurad[at]pm[dot]me
@@ -0,0 +1,266 @@+<# :
+@echo off
+powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))"
+exit /b %errorlevel%
+#>
+
+# ============================================================
+# Transcodificar - conversor de vídeos em lote (FFmpeg)
+# Arquivo único .cmd (auto-executável como PowerShell)
+# ============================================================
+
+try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Transcodificar Vídeos' } catch {}
+
+function Pausar {
+ Write-Host ''
+ Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray
+ [void][System.Console]::ReadLine()
+}
+
+function Titulo($texto) {
+ Write-Host ''
+ Write-Host '============================================================' -ForegroundColor DarkCyan
+ Write-Host " $texto" -ForegroundColor Cyan
+ Write-Host '============================================================' -ForegroundColor DarkCyan
+}
+
+# ------------------------------------------------------------
+# Configuração persistente (lembra última pasta e qualidade)
+# ------------------------------------------------------------
+$configDir = Join-Path $env:APPDATA 'Transcodificar'
+$configFile = Join-Path $configDir 'config.json'
+
+function Carregar-Config {
+ if (Test-Path -LiteralPath $configFile) {
+ try { return Get-Content -LiteralPath $configFile -Raw -Encoding UTF8 | ConvertFrom-Json } catch {}
+ }
+ return $null
+}
+
+function Salvar-Config($pasta, $qualidade) {
+ try {
+ New-Item -ItemType Directory -Path $configDir -Force | Out-Null
+ [pscustomobject]@{ UltimaPasta = $pasta; UltimaQualidade = $qualidade } |
+ ConvertTo-Json | Set-Content -LiteralPath $configFile -Encoding UTF8
+ } catch {}
+}
+
+# ------------------------------------------------------------
+# Perfis de qualidade (o menor/compacto é o último - padrão)
+# ------------------------------------------------------------
+$perfis = @(
+ [pscustomobject]@{ Id=1; Nome='Máxima (resolução original)'; Desc='Mantém a resolução, altíssima qualidade'; Altura=0; Crf=18; Audio='192k' },
+ [pscustomobject]@{ Id=2; Nome='Alta (1080p)'; Desc='Full HD, ótima qualidade'; Altura=1080; Crf=20; Audio='160k' },
+ [pscustomobject]@{ Id=3; Nome='Média (720p)'; Desc='HD, bom equilíbrio tamanho x qualidade'; Altura=720; Crf=23; Audio='128k' },
+ [pscustomobject]@{ Id=4; Nome='Baixa (480p)'; Desc='Arquivo leve, qualidade razoável'; Altura=480; Crf=26; Audio='96k' },
+ [pscustomobject]@{ Id=5; Nome='Mínima (compacta ~320p)'; Desc='Bem pequena, para prévia / upload rápido'; Altura=320; Crf=30; Audio='64k' }
+)
+
+# Formatos de vídeo reconhecidos
+$extensoes = @(
+ '.mp4', '.mkv', '.avi', '.mov', '.wmv',
+ '.flv', '.webm', '.m4v', '.mpg', '.mpeg',
+ '.mts', '.m2ts', '.ts', '.3gp', '.vob'
+)
+
+# ------------------------------------------------------------
+# 1) Verificação de dependências
+# ------------------------------------------------------------
+Titulo 'Transcodificar Vídeos'
+Write-Host 'Verificando o que é necessário...' -ForegroundColor Gray
+
+$ffmpeg = Get-Command ffmpeg -ErrorAction SilentlyContinue
+if (-not $ffmpeg) {
+ Write-Host ''
+ Write-Host '[ERRO] O FFmpeg não foi encontrado no sistema.' -ForegroundColor Red
+ Write-Host 'Ele é necessário para converter os vídeos.' -ForegroundColor Yellow
+ Write-Host ''
+ Write-Host 'Para instalar, abra o Terminal / Prompt e rode:' -ForegroundColor Gray
+ Write-Host ' winget install Gyan.FFmpeg' -ForegroundColor White
+ Write-Host ''
+ Write-Host 'Depois feche e abra este programa novamente.' -ForegroundColor Gray
+ Pausar
+ exit 1
+}
+Write-Host "[OK] FFmpeg encontrado: $($ffmpeg.Source)" -ForegroundColor Green
+
+$config = Carregar-Config
+
+# ------------------------------------------------------------
+# 2) Escolha da pasta OU de um vídeo único (lembra o último)
+# ------------------------------------------------------------
+$entradaPadrao = Join-Path $env:USERPROFILE 'Downloads\Vídeos'
+$entradaAtual = if ($config -and $config.UltimaPasta) { $config.UltimaPasta } else { $entradaPadrao }
+
+$modoArquivo = $false
+$arquivoUnico = $null
+
+while ($true) {
+ Titulo 'Pasta ou vídeo'
+ Write-Host 'Caminho atual:' -ForegroundColor Gray
+ Write-Host " $entradaAtual" -ForegroundColor White
+ Write-Host ''
+ Write-Host 'Enter = confirmar | ou cole/digite uma PASTA ou um ARQUIVO de vídeo' -ForegroundColor DarkGray
+ $resp = Read-Host 'Pasta/arquivo'
+
+ if (-not [string]::IsNullOrWhiteSpace($resp)) {
+ $entradaAtual = $resp.Trim().Trim('"')
+ }
+
+ if (Test-Path -LiteralPath $entradaAtual -PathType Leaf) {
+ $ext = [System.IO.Path]::GetExtension($entradaAtual).ToLowerInvariant()
+ if ($extensoes -notcontains $ext) {
+ Write-Host ''
+ Write-Host "[!] Esse arquivo não é um vídeo suportado ($ext)." -ForegroundColor Yellow
+ continue
+ }
+ $modoArquivo = $true
+ $arquivoUnico = (Resolve-Path -LiteralPath $entradaAtual).Path
+ break
+ }
+ elseif (Test-Path -LiteralPath $entradaAtual -PathType Container) {
+ $modoArquivo = $false
+ break
+ }
+
+ Write-Host ''
+ Write-Host "[!] Caminho não encontrado: $entradaAtual" -ForegroundColor Yellow
+ $criar = Read-Host 'Criar como pasta? (s/N)'
+ if ($criar -match '^(s|sim|y|yes)$') {
+ try {
+ New-Item -ItemType Directory -Path $entradaAtual -Force | Out-Null
+ Write-Host "[OK] Pasta criada." -ForegroundColor Green
+ $modoArquivo = $false
+ break
+ } catch {
+ Write-Host "[ERRO] Não foi possível criar a pasta." -ForegroundColor Red
+ }
+ }
+}
+
+if ($modoArquivo) {
+ $arquivoObj = Get-Item -LiteralPath $arquivoUnico
+ $origem = $arquivoObj.DirectoryName
+} else {
+ $origem = (Resolve-Path -LiteralPath $entradaAtual).Path
+}
+$destino = Join-Path $origem 'Transcodificado'
+
+# ------------------------------------------------------------
+# 3) Escolha da qualidade
+# ------------------------------------------------------------
+$qualidadePadrao = if ($config -and $config.UltimaQualidade) { [int]$config.UltimaQualidade } else { 5 }
+
+Titulo 'Qualidade de saída'
+foreach ($p in $perfis) {
+ $marca = if ($p.Id -eq $qualidadePadrao) { '>' } else { ' ' }
+ $cor = if ($p.Id -eq $qualidadePadrao) { 'White' } else { 'Gray' }
+ Write-Host ("{0} [{1}] {2}" -f $marca, $p.Id, $p.Nome) -ForegroundColor $cor
+ Write-Host (" {0}" -f $p.Desc) -ForegroundColor DarkGray
+}
+Write-Host ''
+Write-Host "Enter = usar a opção padrão [$qualidadePadrao]" -ForegroundColor DarkGray
+
+$perfil = $null
+while (-not $perfil) {
+ $escolha = Read-Host 'Qualidade (1-5)'
+ if ([string]::IsNullOrWhiteSpace($escolha)) { $escolha = "$qualidadePadrao" }
+ $perfil = $perfis | Where-Object { $_.Id -eq ($escolha -as [int]) } | Select-Object -First 1
+ if (-not $perfil) { Write-Host '[!] Escolha inválida. Digite um número de 1 a 5.' -ForegroundColor Yellow }
+}
+
+Salvar-Config $origem $perfil.Id
+
+# ------------------------------------------------------------
+# 4) Coleta dos arquivos de vídeo
+# ------------------------------------------------------------
+if ($modoArquivo) {
+ $arquivos = @($arquivoObj)
+} else {
+ $arquivos = @(Get-ChildItem -LiteralPath $origem -Recurse -File -ErrorAction SilentlyContinue |
+ Where-Object {
+ ($extensoes -contains $_.Extension.ToLowerInvariant()) -and
+ (-not $_.FullName.StartsWith($destino, [System.StringComparison]::OrdinalIgnoreCase))
+ })
+}
+
+Titulo 'Processando'
+Write-Host "Pasta: $origem"
+Write-Host "Qualidade: $($perfil.Nome) (CRF $($perfil.Crf), áudio $($perfil.Audio))"
+Write-Host "Destino: $destino"
+Write-Host "Vídeos encontrados: $($arquivos.Count)" -ForegroundColor Cyan
+
+if ($arquivos.Count -eq 0) {
+ Write-Host ''
+ Write-Host 'Nenhum vídeo para converter nessa pasta.' -ForegroundColor Yellow
+ Pausar
+ exit 0
+}
+
+New-Item -ItemType Directory -Path $destino -Force | Out-Null
+
+# Filtro de escala conforme o perfil (nunca faz upscaling)
+if ($perfil.Altura -eq 0) {
+ $vf = 'scale=trunc(iw/2)*2:trunc(ih/2)*2'
+} else {
+ $vf = "scale=-2:trunc(min($($perfil.Altura)\,ih)/2)*2"
+}
+
+$convertidos = 0
+$ignorados = 0
+$erros = 0
+$indice = 0
+
+foreach ($arquivo in $arquivos) {
+ $indice++
+
+ $relativo = $arquivo.FullName.Substring($origem.Length).TrimStart('\')
+ $subpasta = Split-Path -Path $relativo -Parent
+ $pastaSaida = if ([string]::IsNullOrWhiteSpace($subpasta)) { $destino } else { Join-Path $destino $subpasta }
+ New-Item -ItemType Directory -Path $pastaSaida -Force | Out-Null
+
+ $saida = Join-Path $pastaSaida ($arquivo.BaseName + '.mp4')
+
+ Write-Host ''
+ Write-Host ("[{0}/{1}]" -f $indice, $arquivos.Count) -ForegroundColor Magenta -NoNewline
+ Write-Host " $($arquivo.Name)"
+
+ if (Test-Path -LiteralPath $saida) {
+ Write-Host " [IGNORADO] Já existe na pasta de destino." -ForegroundColor Yellow
+ $ignorados++
+ continue
+ }
+
+ $ffArgs = @(
+ '-hide_banner', '-nostdin',
+ '-i', $arquivo.FullName,
+ '-map', '0:v:0', '-map', '0:a?',
+ '-vf', $vf,
+ '-c:v', 'libx264', '-preset', 'medium', '-crf', "$($perfil.Crf)", '-pix_fmt', 'yuv420p',
+ '-c:a', 'aac', '-b:a', $perfil.Audio, '-ac', '2',
+ '-movflags', '+faststart',
+ '-y', $saida
+ )
+
+ & ffmpeg @ffArgs
+
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host " [OK] Convertido." -ForegroundColor Green
+ $convertidos++
+ } else {
+ Write-Host " [ERRO] Falha ao converter." -ForegroundColor Red
+ if (Test-Path -LiteralPath $saida) { Remove-Item -LiteralPath $saida -Force -ErrorAction SilentlyContinue }
+ $erros++
+ }
+}
+
+# ------------------------------------------------------------
+# 5) Resumo final
+# ------------------------------------------------------------
+Titulo 'Concluído'
+Write-Host "Convertidos: $convertidos" -ForegroundColor Green
+Write-Host "Ignorados: $ignorados" -ForegroundColor Yellow
+Write-Host "Erros: $erros" -ForegroundColor $(if ($erros -gt 0) { 'Red' } else { 'Gray' })
+Write-Host "Destino: $destino"
+Pausar