first commit
Pablo Murad pablo@pablomurad.com
Fri, 11 Sep 2026 14:14:19 -0300
4 files changed,
667 insertions(+),
0 deletions(-)
A
.gitignore
@@ -0,0 +1,50 @@
+# Sistema / editor +Thumbs.db +Desktop.ini +.DS_Store +*.swp +*.tmp +*~ +.vscode/ +.idea/ + +# Segredos e ambiente (por precaução) +.env +.env.* +*.pem +*.key +*.pfx +credentials.json +secrets.* + +# Saídas dos scripts (se rodarem dentro do repo) +Convertido/ +Transcodificado/ + +# Mídia que possa cair na pasta por acidente +*.jpg +*.jpeg +*.png +*.webp +*.bmp +*.gif +*.tif +*.tiff +*.heic +*.heif +*.avif +*.mp4 +*.mkv +*.avi +*.mov +*.wmv +*.flv +*.webm +*.m4v +*.mpg +*.mpeg +*.mts +*.m2ts +*.ts +*.3gp +*.vob
A
Imagens/converter-imagens.cmd
@@ -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
A
README.md
@@ -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
A
Vídeos/transcodificar.cmd
@@ -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