first commit
Pablo Murad pablo@pablomurad.com
Fri, 11 Sep 2026 14:55:01 -0300
25 files changed,
1582 insertions(+),
14 deletions(-)
jump to
M
Imagens/converter-imagens.cmd
→
Imagens/converter-imagens.cmd
@@ -68,6 +68,8 @@ # ------------------------------------------------------------
# 1) Verificação de dependências # ------------------------------------------------------------ Titulo 'Converter Imagens' +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray +Write-Host '' Write-Host 'Verificando o que é necessário...' -ForegroundColor Gray $magick = Get-Command magick -ErrorAction SilentlyContinue
A
Imagens/imagens-e-pdf.cmd
@@ -0,0 +1,104 @@
+<# : +@echo off +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Imagens e PDF - junta imagens em PDF, ou PDF em imagens (ImageMagick) +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = 'Imagens e PDF' } catch {} + +function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() } +function Titulo($t) { + Write-Host '' + Write-Host '============================================================' -ForegroundColor DarkCyan + Write-Host " $t" -ForegroundColor Cyan + Write-Host '============================================================' -ForegroundColor DarkCyan +} + +$extImg = @('.jpg','.jpeg','.png','.webp','.bmp','.tif','.tiff') + +Titulo 'Imagens e PDF' +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray +Write-Host '' +Write-Host 'Verificando o que é necessário...' -ForegroundColor Gray +$magick = Get-Command magick -ErrorAction SilentlyContinue +if (-not $magick) { Write-Host ''; Write-Host '[ERRO] ImageMagick não encontrado.' -ForegroundColor Red; Write-Host 'Instale com: winget install ImageMagick.ImageMagick' -ForegroundColor White; Pausar; exit 1 } +Write-Host "[OK] ImageMagick: $($magick.Source)" -ForegroundColor Green + +Titulo 'O que você quer fazer?' +Write-Host ' [1] Imagens -> PDF (junta todas as imagens de uma pasta num PDF)' -ForegroundColor White +Write-Host ' [2] PDF -> Imagens (salva cada página como imagem)' -ForegroundColor White +$acao = $null +while (-not $acao) { $a = Read-Host 'Opção (1-2)'; switch ($a.Trim()) { '1'{$acao='img2pdf'} '2'{$acao='pdf2img'} default{Write-Host '[!] Digite 1 ou 2.' -ForegroundColor Yellow} } } + +if ($acao -eq 'img2pdf') { + # ---- Imagens -> PDF ---- + $pasta = $null + while (-not $pasta) { + Titulo 'Pasta com as imagens' + $p = (Read-Host 'Cole o caminho da pasta').Trim().Trim('"') + if (Test-Path -LiteralPath $p -PathType Container) { $pasta = (Resolve-Path -LiteralPath $p).Path } else { Write-Host '[!] Pasta não encontrada.' -ForegroundColor Yellow } + } + $imagens = @(Get-ChildItem -LiteralPath $pasta -File | Where-Object { $extImg -contains $_.Extension.ToLowerInvariant() } | Sort-Object Name) + if ($imagens.Count -eq 0) { Write-Host 'Nenhuma imagem nessa pasta.' -ForegroundColor Yellow; Pausar; exit 0 } + Write-Host '' + Write-Host "$($imagens.Count) imagens encontradas (em ordem alfabética)." -ForegroundColor Cyan + $nome = (Read-Host 'Nome do PDF de saída (Enter = saida.pdf)').Trim() + if ([string]::IsNullOrWhiteSpace($nome)) { $nome = 'saida.pdf' } + if ($nome -notmatch '\.pdf$') { $nome += '.pdf' } + $pdf = Join-Path $pasta $nome + Write-Host ''; Write-Host "Gerando: $pdf ..." -ForegroundColor Cyan + $imgArgs = @($imagens.FullName) + @('-auto-orient', $pdf) + & magick @imgArgs + if ($LASTEXITCODE -eq 0 -and (Test-Path -LiteralPath $pdf)) { + Titulo 'Concluído' + Write-Host "[OK] PDF criado com $($imagens.Count) páginas." -ForegroundColor Green + Write-Host "Arquivo: $pdf" + } else { Write-Host '[ERRO] Falha ao gerar o PDF.' -ForegroundColor Red } + Pausar; exit 0 +} + +# ---- PDF -> Imagens ---- +$pdfAlvo = $null; $modoArquivo = $false +while (-not $pdfAlvo) { + Titulo 'PDF de entrada' + $p = (Read-Host 'Cole o caminho de um PDF ou de uma pasta com PDFs').Trim().Trim('"') + if ((Test-Path -LiteralPath $p -PathType Leaf) -and ($p -match '\.pdf$')) { $pdfAlvo = (Resolve-Path -LiteralPath $p).Path; $modoArquivo = $true } + elseif (Test-Path -LiteralPath $p -PathType Container) { $pdfAlvo = (Resolve-Path -LiteralPath $p).Path; $modoArquivo = $false } + else { Write-Host '[!] Caminho inválido (informe um .pdf ou uma pasta).' -ForegroundColor Yellow } +} +Titulo 'Formato das imagens' +Write-Host ' [1] PNG (sem perdas) [2] JPG (menor)' -ForegroundColor Gray +$fmt = Read-Host 'Formato (Enter = 1)' +$extSaida = if ($fmt.Trim() -eq '2') { '.jpg' } else { '.png' } +$dpiEnt = Read-Host 'Qualidade em DPI (Enter = 150; 300 = alta)' +$dpi = if (($dpiEnt.Trim()) -and ($dpiEnt -as [int])) { [int]$dpiEnt } else { 150 } + +if ($modoArquivo) { $pdfs = @(Get-Item -LiteralPath $pdfAlvo) } else { $pdfs = @(Get-ChildItem -LiteralPath $pdfAlvo -File | Where-Object { $_.Extension.ToLowerInvariant() -eq '.pdf' }) } +if ($pdfs.Count -eq 0) { Write-Host 'Nenhum PDF encontrado.' -ForegroundColor Yellow; Pausar; exit 0 } + +Titulo 'Processando' +$ok=0;$err=0 +foreach ($pf in $pdfs) { + $outDir = Join-Path $pf.DirectoryName ($pf.BaseName + '-imagens') + New-Item -ItemType Directory -Path $outDir -Force | Out-Null + $padraoSaida = Join-Path $outDir ('pagina-%03d' + $extSaida) + Write-Host ''; Write-Host $pf.Name -ForegroundColor Magenta + $imgArgs = @('-density',"$dpi",$pf.FullName) + if ($extSaida -eq '.jpg') { $imgArgs += @('-quality','90','-background','white','-alpha','remove') } + $imgArgs += $padraoSaida + & magick @imgArgs + if ($LASTEXITCODE -eq 0) { + $qtd = @(Get-ChildItem -LiteralPath $outDir -File).Count + Write-Host " [OK] $qtd página(s) -> $outDir" -ForegroundColor Green; $ok++ + } else { Write-Host ' [ERRO] Falha ao converter.' -ForegroundColor Red; $err++ } +} +Titulo 'Concluído' +Write-Host "PDFs convertidos: $ok" -ForegroundColor Green +Write-Host "Erros: $err" -ForegroundColor $(if ($err -gt 0){'Red'}else{'Gray'}) +Pausar
A
Imagens/marca-dagua.cmd
@@ -0,0 +1,134 @@
+<# : +@echo off +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Marca d'água - aplica texto ou logo em imagens (ImageMagick) +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = "Marca d'água" } catch {} + +function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() } +function Titulo($t) { + Write-Host '' + Write-Host '============================================================' -ForegroundColor DarkCyan + Write-Host " $t" -ForegroundColor Cyan + Write-Host '============================================================' -ForegroundColor DarkCyan +} + +$configDir = Join-Path $env:APPDATA 'MarcaDagua' +$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($o) { try { New-Item -ItemType Directory -Path $configDir -Force | Out-Null; $o | ConvertTo-Json | Set-Content -LiteralPath $configFile -Encoding UTF8 } catch {} } + +$extensoes = @('.jpg','.jpeg','.png','.webp','.bmp','.tif','.tiff') +$posicoes = @( + [pscustomobject]@{ Id=1; Nome='Inferior direita'; Gravity='SouthEast' }, + [pscustomobject]@{ Id=2; Nome='Inferior esquerda'; Gravity='SouthWest' }, + [pscustomobject]@{ Id=3; Nome='Superior direita'; Gravity='NorthEast' }, + [pscustomobject]@{ Id=4; Nome='Superior esquerda'; Gravity='NorthWest' }, + [pscustomobject]@{ Id=5; Nome='Centro'; Gravity='Center' } +) + +Titulo "Marca d'água" +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray +Write-Host '' +Write-Host 'Verificando o que é necessário...' -ForegroundColor Gray +$magick = Get-Command magick -ErrorAction SilentlyContinue +if (-not $magick) { Write-Host ''; Write-Host '[ERRO] ImageMagick não encontrado.' -ForegroundColor Red; Write-Host 'Instale com: winget install ImageMagick.ImageMagick' -ForegroundColor White; Pausar; exit 1 } +Write-Host "[OK] ImageMagick: $($magick.Source)" -ForegroundColor Green +$config = Carregar-Config + +# Pasta ou imagem +$entradaAtual = if ($config -and $config.UltimaEntrada) { $config.UltimaEntrada } else { Join-Path $env:USERPROFILE 'Downloads\Imagens' } +$modoArquivo = $false; $arquivoUnico = $null +while ($true) { + Titulo 'Pasta ou imagem' + Write-Host " $entradaAtual" -ForegroundColor White + Write-Host 'Enter = confirmar | ou cole 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) { + if ($extensoes -notcontains [System.IO.Path]::GetExtension($entradaAtual).ToLowerInvariant()) { Write-Host '[!] Não é uma imagem suportada.' -ForegroundColor Yellow; continue } + $modoArquivo = $true; $arquivoUnico = (Resolve-Path -LiteralPath $entradaAtual).Path; break + } elseif (Test-Path -LiteralPath $entradaAtual -PathType Container) { $modoArquivo = $false; break } + Write-Host '[!] Caminho não encontrado.' -ForegroundColor Yellow +} +if ($modoArquivo) { $arquivoObj = Get-Item -LiteralPath $arquivoUnico; $origem = $arquivoObj.DirectoryName } else { $origem = (Resolve-Path -LiteralPath $entradaAtual).Path } +$destino = Join-Path $origem 'ComMarca' + +# Tipo de marca +Titulo "Tipo de marca d'água" +Write-Host ' [1] Texto' -ForegroundColor White +Write-Host ' [2] Logo (arquivo PNG, de preferência com fundo transparente)' -ForegroundColor White +$tipo = $null +while (-not $tipo) { $t = Read-Host 'Tipo (1-2)'; switch ($t.Trim()) { '1'{$tipo='texto'} '2'{$tipo='logo'} default{Write-Host '[!] Digite 1 ou 2.' -ForegroundColor Yellow} } } + +$texto = $null; $logo = $null; $pct = 20 +if ($tipo -eq 'texto') { + while ([string]::IsNullOrWhiteSpace($texto)) { $texto = Read-Host 'Texto da marca' } +} else { + while (-not $logo) { + $l = (Read-Host 'Caminho do logo (PNG)').Trim().Trim('"') + if (Test-Path -LiteralPath $l -PathType Leaf) { $logo = (Resolve-Path -LiteralPath $l).Path } else { Write-Host '[!] Arquivo não encontrado.' -ForegroundColor Yellow } + } + $p = Read-Host 'Tamanho do logo em % da largura (Enter = 20)' + if (-not [string]::IsNullOrWhiteSpace($p) -and ($p -as [int])) { $pct = [int]$p } +} + +# Posição +Titulo 'Posição' +foreach ($ps in $posicoes) { Write-Host (" [{0}] {1}" -f $ps.Id,$ps.Nome) -ForegroundColor Gray } +$posicao = $null +while (-not $posicao) { $e = Read-Host 'Posição (1-5, Enter = 1)'; if ([string]::IsNullOrWhiteSpace($e)) { $e = '1' }; $posicao = $posicoes | Where-Object { $_.Id -eq ($e -as [int]) } | Select-Object -First 1; if (-not $posicao) { Write-Host '[!] Inválido.' -ForegroundColor Yellow } } +$grav = $posicao.Gravity +$offset = if ($grav -eq 'Center') { '+0+0' } else { '+25+25' } + +Salvar-Config ([pscustomobject]@{ UltimaEntrada=$origem; UltimoTipo=$tipo; UltimoTexto=$texto; UltimoLogo=$logo; UltimaPosicao=$posicao.Id }) + +# Coleta +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 "Marca: $tipo | Posição: $($posicao.Nome) | Destino: $destino" +Write-Host "Imagens encontradas: $($arquivos.Count)" -ForegroundColor Cyan +if ($arquivos.Count -eq 0) { Write-Host 'Nenhuma imagem encontrada.' -ForegroundColor Yellow; Pausar; exit 0 } +New-Item -ItemType Directory -Path $destino -Force | Out-Null + +$ok=0;$ign=0;$err=0;$i=0 +foreach ($a in $arquivos) { + $i++ + $rel = $a.FullName.Substring($origem.Length).TrimStart('\'); $sub = Split-Path $rel -Parent + $pastaSaida = if ([string]::IsNullOrWhiteSpace($sub)) { $destino } else { Join-Path $destino $sub } + New-Item -ItemType Directory -Path $pastaSaida -Force | Out-Null + $saida = Join-Path $pastaSaida $a.Name + Write-Host ''; Write-Host ("[{0}/{1}] {2}" -f $i,$arquivos.Count,$a.Name) -ForegroundColor Magenta + if (Test-Path -LiteralPath $saida) { Write-Host ' [IGNORADO] Já existe.' -ForegroundColor Yellow; $ign++; continue } + + if ($tipo -eq 'texto') { + $w = [int](& magick identify -format '%w' "$($a.FullName)[0]" 2>$null) + if (-not $w -or $w -le 0) { $w = 1000 } + $ps = [int]($w / 22); if ($ps -lt 12) { $ps = 12 } + $imgArgs = @($a.FullName,'-gravity',$grav,'-pointsize',"$ps",'-fill','rgba(255,255,255,0.85)','-undercolor','rgba(0,0,0,0.35)','-annotate',$offset,$texto,$saida) + } else { + $w = [int](& magick identify -format '%w' "$($a.FullName)[0]" 2>$null) + if (-not $w -or $w -le 0) { $w = 1000 } + $lw = [int]($w * $pct / 100); if ($lw -lt 10) { $lw = 10 } + $imgArgs = @($a.FullName,'(',$logo,'-resize',"${lw}x",')','-gravity',$grav,'-geometry',$offset,'-composite',$saida) + } + & magick @imgArgs + if ($LASTEXITCODE -eq 0 -and (Test-Path -LiteralPath $saida)) { Write-Host ' [OK] Marca aplicada.' -ForegroundColor Green; $ok++ } + else { Write-Host ' [ERRO] Falha.' -ForegroundColor Red; if (Test-Path -LiteralPath $saida) { Remove-Item -LiteralPath $saida -Force -ErrorAction SilentlyContinue }; $err++ } +} +Titulo 'Concluído' +Write-Host "Aplicadas: $ok" -ForegroundColor Green +Write-Host "Ignoradas: $ign" -ForegroundColor Yellow +Write-Host "Erros: $err" -ForegroundColor $(if ($err -gt 0){'Red'}else{'Gray'}) +Write-Host "Destino: $destino" +Pausar
A
Organizar/achar-duplicados.cmd
@@ -0,0 +1,108 @@
+<# : +@echo off +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Achar duplicados - encontra arquivos iguais por conteúdo (hash) +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = 'Achar duplicados' } catch {} + +function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() } +function Titulo($t) { + Write-Host '' + Write-Host '============================================================' -ForegroundColor DarkCyan + Write-Host " $t" -ForegroundColor Cyan + Write-Host '============================================================' -ForegroundColor DarkCyan +} +function Tam($bytes) { if ($bytes -ge 1GB) { '{0:N2} GB' -f ($bytes/1GB) } elseif ($bytes -ge 1MB) { '{0:N1} MB' -f ($bytes/1MB) } else { '{0:N0} KB' -f ($bytes/1KB) } } + +Titulo 'Achar duplicados' +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray + +$pasta = $null +while (-not $pasta) { + Write-Host '' + $p = (Read-Host 'Cole o caminho da pasta (verifica também as subpastas)').Trim().Trim('"') + if (Test-Path -LiteralPath $p -PathType Container) { $pasta = (Resolve-Path -LiteralPath $p).Path } else { Write-Host '[!] Pasta não encontrada.' -ForegroundColor Yellow } +} + +Write-Host '' +Write-Host 'Analisando... (agrupa por tamanho e depois compara o conteúdo)' -ForegroundColor Gray +$todos = @(Get-ChildItem -LiteralPath $pasta -Recurse -File -ErrorAction SilentlyContinue | Where-Object { $_.Length -gt 0 }) + +# 1) agrupa por tamanho (rápido); 2) só calcula hash de quem tem tamanho repetido +$porTamanho = $todos | Group-Object Length | Where-Object { $_.Count -gt 1 } +$grupos = @() +foreach ($g in $porTamanho) { + $porHash = $g.Group | Group-Object { (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash } + foreach ($h in ($porHash | Where-Object { $_.Count -gt 1 })) { + $grupos += ,@($h.Group | Sort-Object FullName) + } +} + +if ($grupos.Count -eq 0) { Titulo 'Resultado'; Write-Host 'Nenhum arquivo duplicado encontrado.' -ForegroundColor Green; Pausar; exit 0 } + +$totalDup = 0; $espaco = 0 +foreach ($grp in $grupos) { $totalDup += ($grp.Count - 1); $espaco += ($grp.Count - 1) * $grp[0].Length } + +Titulo 'Duplicados encontrados' +Write-Host ("Grupos de iguais: {0} | Cópias extras: {1} | Espaço recuperável: {2}" -f $grupos.Count, $totalDup, (Tam $espaco)) -ForegroundColor Cyan +Write-Host '' +$mostra = [Math]::Min($grupos.Count, 15) +for ($i=0; $i -lt $mostra; $i++) { + $grp = $grupos[$i] + Write-Host ("Grupo {0} ({1} cópias, {2} cada):" -f ($i+1), $grp.Count, (Tam $grp[0].Length)) -ForegroundColor White + for ($j=0; $j -lt $grp.Count; $j++) { + $marca = if ($j -eq 0) { '[manter] ' } else { '[extra] ' } + $cor = if ($j -eq 0) { 'Green' } else { 'DarkGray' } + Write-Host (" $marca" + $grp[$j].FullName) -ForegroundColor $cor + } +} +if ($grupos.Count -gt $mostra) { Write-Host ("... e mais {0} grupo(s)." -f ($grupos.Count - $mostra)) -ForegroundColor DarkGray } + +Titulo 'O que fazer com as cópias extras?' +Write-Host ' [1] Só listar (nada é alterado) - padrão' -ForegroundColor White +Write-Host ' [2] Mover as cópias extras para uma subpasta "_Duplicados"' -ForegroundColor White +Write-Host ' [3] Apagar as cópias extras (mantém sempre 1 original)' -ForegroundColor White +$acao = Read-Host 'Opção (Enter = 1)' +if ([string]::IsNullOrWhiteSpace($acao)) { $acao = '1' } + +if ($acao -eq '1') { Write-Host ''; Write-Host 'Nada foi alterado. Relatório acima.' -ForegroundColor Gray; Pausar; exit 0 } + +if ($acao -eq '3') { + Write-Host '' + Write-Host 'ATENÇÃO: isto APAGA as cópias extras permanentemente.' -ForegroundColor Red + $conf = Read-Host 'Digite APAGAR para confirmar' + if ($conf -cne 'APAGAR') { Write-Host 'Cancelado.' -ForegroundColor Yellow; Pausar; exit 0 } +} + +$feitos=0;$err=0 +$destDup = Join-Path $pasta '_Duplicados' +foreach ($grp in $grupos) { + for ($j=1; $j -lt $grp.Count; $j++) { + $f = $grp[$j] + try { + if ($acao -eq '2') { + New-Item -ItemType Directory -Path $destDup -Force | Out-Null + $base=[System.IO.Path]::GetFileNameWithoutExtension($f.Name); $ext=$f.Extension + $dest=Join-Path $destDup $f.Name; $n=1 + while (Test-Path -LiteralPath $dest) { $dest=Join-Path $destDup ("$base ($n)$ext"); $n++ } + Move-Item -LiteralPath $f.FullName -Destination $dest -ErrorAction Stop + } else { + Remove-Item -LiteralPath $f.FullName -Force -ErrorAction Stop + } + $feitos++ + } catch { Write-Host " [ERRO] $($f.Name): $($_.Exception.Message)" -ForegroundColor Red; $err++ } + } +} +Titulo 'Concluído' +$verbo = if ($acao -eq '2') { 'Movidas' } else { 'Apagadas' } +Write-Host "$verbo`: $feitos cópia(s) extra(s)" -ForegroundColor Green +Write-Host "Erros: $err" -ForegroundColor $(if ($err -gt 0){'Red'}else{'Gray'}) +if ($acao -eq '2') { Write-Host "Movidos para: $destDup" } +Pausar
A
Organizar/organizar-por-tipo.cmd
@@ -0,0 +1,83 @@
+<# : +@echo off +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Organizar por tipo - separa arquivos em subpastas por categoria +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = 'Organizar por tipo' } catch {} + +function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() } +function Titulo($t) { + Write-Host '' + Write-Host '============================================================' -ForegroundColor DarkCyan + Write-Host " $t" -ForegroundColor Cyan + Write-Host '============================================================' -ForegroundColor DarkCyan +} +function Nome-Livre($dir,$nome) { + $base=[System.IO.Path]::GetFileNameWithoutExtension($nome); $ext=[System.IO.Path]::GetExtension($nome) + $t=Join-Path $dir $nome; $n=1 + while (Test-Path -LiteralPath $t) { $t=Join-Path $dir ("$base ($n)$ext"); $n++ } + return $t +} + +# Mapa extensão -> categoria +$categorias = [ordered]@{ + 'Imagens' = @('.jpg','.jpeg','.png','.gif','.webp','.bmp','.tif','.tiff','.heic','.heif','.svg','.raw','.cr2','.nef') + 'Vídeos' = @('.mp4','.mkv','.avi','.mov','.wmv','.flv','.webm','.m4v','.mpg','.mpeg','.ts','.3gp','.vob') + 'Áudio' = @('.mp3','.m4a','.wav','.flac','.aac','.ogg','.wma','.opus') + 'Documentos' = @('.pdf','.doc','.docx','.xls','.xlsx','.ppt','.pptx','.txt','.odt','.ods','.csv','.rtf','.md','.epub') + 'Compactados' = @('.zip','.rar','.7z','.tar','.gz','.bz2','.xz') + 'Programas' = @('.exe','.msi','.bat','.cmd','.ps1') +} + +Titulo 'Organizar por tipo' +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray + +$pasta = $null +while (-not $pasta) { + Write-Host '' + $p = (Read-Host 'Cole o caminho da pasta a organizar').Trim().Trim('"') + if (Test-Path -LiteralPath $p -PathType Container) { $pasta = (Resolve-Path -LiteralPath $p).Path } else { Write-Host '[!] Pasta não encontrada.' -ForegroundColor Yellow } +} + +# Só arquivos no primeiro nível (não entra em subpastas) +$nomesCategorias = @($categorias.Keys) + 'Outros' +$arquivos = @(Get-ChildItem -LiteralPath $pasta -File) +if ($arquivos.Count -eq 0) { Write-Host 'Nenhum arquivo solto nessa pasta.' -ForegroundColor Yellow; Pausar; exit 0 } + +# Descobre a categoria de cada arquivo +function Categoria-De($ext) { + foreach ($cat in $categorias.Keys) { if ($categorias[$cat] -contains $ext) { return $cat } } + return 'Outros' +} +$planos = foreach ($a in $arquivos) { + [pscustomobject]@{ Arquivo=$a; Categoria=(Categoria-De $a.Extension.ToLowerInvariant()) } +} + +Titulo 'Prévia' +$planos | Group-Object Categoria | Sort-Object Name | ForEach-Object { + Write-Host (" {0,-14} {1} arquivo(s)" -f $_.Name, $_.Count) -ForegroundColor Gray +} +Write-Host '' +$ok = Read-Host "Mover $($arquivos.Count) arquivo(s) para subpastas por categoria? (s/N)" +if ($ok -notmatch '^(s|sim|y|yes)$') { Write-Host 'Cancelado.' -ForegroundColor Yellow; Pausar; exit 0 } + +$movidos=0;$err=0 +foreach ($pl in $planos) { + $destPasta = Join-Path $pasta $pl.Categoria + New-Item -ItemType Directory -Path $destPasta -Force | Out-Null + $destino = Nome-Livre $destPasta $pl.Arquivo.Name + try { Move-Item -LiteralPath $pl.Arquivo.FullName -Destination $destino -ErrorAction Stop; $movidos++ } + catch { Write-Host " [ERRO] $($pl.Arquivo.Name): $($_.Exception.Message)" -ForegroundColor Red; $err++ } +} +Titulo 'Concluído' +Write-Host "Movidos: $movidos" -ForegroundColor Green +Write-Host "Erros: $err" -ForegroundColor $(if ($err -gt 0){'Red'}else{'Gray'}) +Write-Host "Pasta: $pasta" +Pausar
A
Organizar/renomear-em-lote.cmd
@@ -0,0 +1,144 @@
+<# : +@echo off +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Renomear em lote - por sequência, data ou localizar/substituir +# Também organiza fotos/arquivos por data em pastas +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = 'Renomear em lote' } catch {} + +function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() } +function Titulo($t) { + Write-Host '' + Write-Host '============================================================' -ForegroundColor DarkCyan + Write-Host " $t" -ForegroundColor Cyan + Write-Host '============================================================' -ForegroundColor DarkCyan +} +function Nome-Livre($dir,$nome,$reservados) { + $base=[System.IO.Path]::GetFileNameWithoutExtension($nome); $ext=[System.IO.Path]::GetExtension($nome) + $t=Join-Path $dir $nome; $n=1 + while ((Test-Path -LiteralPath $t) -or ($reservados -contains $t)) { $t=Join-Path $dir ("$base ($n)$ext"); $n++ } + return $t +} + +$magick = Get-Command magick -ErrorAction SilentlyContinue +$extImgExif = @('.jpg','.jpeg','.tif','.tiff','.heic','.heif','.png','.webp') +function Data-Do-Arquivo($file) { + if ($magick -and ($extImgExif -contains $file.Extension.ToLowerInvariant())) { + try { + $d = & magick identify -format '%[EXIF:DateTimeOriginal]' "$($file.FullName)[0]" 2>$null + if ($d -and ($d.Trim() -match '^\d{4}:\d{2}:\d{2}\s\d{2}:\d{2}:\d{2}$')) { + return [datetime]::ParseExact($d.Trim(),'yyyy:MM:dd HH:mm:ss',$null) + } + } catch {} + } + return $file.LastWriteTime +} + +Titulo 'Renomear em lote' +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray + +$pasta = $null +while (-not $pasta) { + Write-Host '' + $p = (Read-Host 'Cole o caminho da pasta').Trim().Trim('"') + if (Test-Path -LiteralPath $p -PathType Container) { $pasta = (Resolve-Path -LiteralPath $p).Path } else { Write-Host '[!] Pasta não encontrada.' -ForegroundColor Yellow } +} +$arquivos = @(Get-ChildItem -LiteralPath $pasta -File | Sort-Object Name) +if ($arquivos.Count -eq 0) { Write-Host 'Nenhum arquivo nessa pasta.' -ForegroundColor Yellow; Pausar; exit 0 } + +Titulo 'Modo de renomeação' +Write-Host ' [1] Sequência (ex.: Foto_001, Foto_002, ...)' -ForegroundColor White +Write-Host ' [2] Data/hora (ex.: 2026-09-11_14-30-05)' -ForegroundColor White +Write-Host ' [3] Localizar e substituir no nome' -ForegroundColor White +Write-Host ' [4] Mover para pastas por data (AAAA-MM)' -ForegroundColor White +$modo = $null +while (-not $modo) { $m = Read-Host 'Modo (1-4)'; if ('1','2','3','4' -contains $m.Trim()) { $modo = $m.Trim() } else { Write-Host '[!] Digite 1 a 4.' -ForegroundColor Yellow } } + +# Monta o plano: cada item = { Origem ; Destino(caminho completo) } +$reservados = @() +$plano = @() + +switch ($modo) { + '1' { + $prefixo = Read-Host 'Prefixo (ex.: Foto)'; if ([string]::IsNullOrWhiteSpace($prefixo)) { $prefixo = 'Arquivo' } + $iniEnt = Read-Host 'Número inicial (Enter = 1)'; $ini = if ($iniEnt -as [int]) { [int]$iniEnt } else { 1 } + $digEnt = Read-Host 'Quantos dígitos (Enter = 3)'; $dig = if ($digEnt -as [int]) { [int]$digEnt } else { 3 } + $c = $ini + foreach ($a in $arquivos) { + $novo = ('{0}_{1}{2}' -f $prefixo, ($c.ToString().PadLeft($dig,'0')), $a.Extension.ToLowerInvariant()) + $dest = Nome-Livre $pasta $novo $reservados; $reservados += $dest + $plano += [pscustomobject]@{ Origem=$a; Destino=$dest }; $c++ + } + } + '2' { + foreach ($a in $arquivos) { + $dt = Data-Do-Arquivo $a + $novo = ('{0}{1}' -f $dt.ToString('yyyy-MM-dd_HH-mm-ss'), $a.Extension.ToLowerInvariant()) + $dest = Nome-Livre $pasta $novo $reservados; $reservados += $dest + $plano += [pscustomobject]@{ Origem=$a; Destino=$dest } + } + } + '3' { + $de = Read-Host 'Localizar (texto no nome)' + if ([string]::IsNullOrEmpty($de)) { Write-Host 'Nada para localizar.' -ForegroundColor Yellow; Pausar; exit 0 } + $para = Read-Host 'Substituir por (pode deixar vazio para remover)' + foreach ($a in $arquivos) { + if ($a.Name -like "*$de*") { + $novoNome = $a.Name.Replace($de,$para) + if ([string]::IsNullOrWhiteSpace([System.IO.Path]::GetFileNameWithoutExtension($novoNome))) { continue } + $dest = Nome-Livre $pasta $novoNome $reservados; $reservados += $dest + $plano += [pscustomobject]@{ Origem=$a; Destino=$dest } + } + } + if ($plano.Count -eq 0) { Write-Host "Nenhum arquivo contém '$de'." -ForegroundColor Yellow; Pausar; exit 0 } + } + '4' { + foreach ($a in $arquivos) { + $dt = Data-Do-Arquivo $a + $subPasta = Join-Path $pasta $dt.ToString('yyyy-MM') + $dest = Join-Path $subPasta $a.Name + $plano += [pscustomobject]@{ Origem=$a; Destino=$dest } + } + } +} + +# Prévia +Titulo 'Prévia' +$acao = if ($modo -eq '4') { 'MOVER' } else { 'RENOMEAR' } +$mostra = [Math]::Min($plano.Count, 12) +for ($i=0; $i -lt $mostra; $i++) { + $rel = $plano[$i].Destino.Substring($pasta.Length).TrimStart('\') + Write-Host (" {0}" -f $plano[$i].Origem.Name) -ForegroundColor Gray -NoNewline + Write-Host (" -> {0}" -f $rel) -ForegroundColor White +} +if ($plano.Count -gt $mostra) { Write-Host (" ... e mais {0} arquivo(s)." -f ($plano.Count - $mostra)) -ForegroundColor DarkGray } +Write-Host '' +$ok = Read-Host "$acao $($plano.Count) arquivo(s)? (s/N)" +if ($ok -notmatch '^(s|sim|y|yes)$') { Write-Host 'Cancelado.' -ForegroundColor Yellow; Pausar; exit 0 } + +$feitos=0;$err=0 +foreach ($p in $plano) { + try { + if ($modo -eq '4') { + $dir = Split-Path $p.Destino -Parent + New-Item -ItemType Directory -Path $dir -Force | Out-Null + $destFinal = Nome-Livre $dir (Split-Path $p.Destino -Leaf) @() + Move-Item -LiteralPath $p.Origem.FullName -Destination $destFinal -ErrorAction Stop + } else { + Rename-Item -LiteralPath $p.Origem.FullName -NewName (Split-Path $p.Destino -Leaf) -ErrorAction Stop + } + $feitos++ + } catch { Write-Host " [ERRO] $($p.Origem.Name): $($_.Exception.Message)" -ForegroundColor Red; $err++ } +} +Titulo 'Concluído' +Write-Host "Processados: $feitos" -ForegroundColor Green +Write-Host "Erros: $err" -ForegroundColor $(if ($err -gt 0){'Red'}else{'Gray'}) +Write-Host "Pasta: $pasta" +Pausar
M
README.md
→
README.md
@@ -6,20 +6,6 @@ 
  -## 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/criar-gif.cmd
@@ -0,0 +1,128 @@
+<# : +@echo off +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Criar GIF / Slideshow - vídeo -> GIF, ou fotos -> vídeo (FFmpeg) +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = 'Criar GIF / Slideshow' } catch {} + +function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() } +function Titulo($t) { + Write-Host '' + Write-Host '============================================================' -ForegroundColor DarkCyan + Write-Host " $t" -ForegroundColor Cyan + Write-Host '============================================================' -ForegroundColor DarkCyan +} + +$extVideo = @('.mp4','.mkv','.avi','.mov','.wmv','.flv','.webm','.m4v','.mpg','.mpeg','.ts','.3gp','.vob') +$extImg = @('.jpg','.jpeg','.png','.webp','.bmp','.tif','.tiff') + +Titulo 'Criar GIF / Slideshow' +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray +Write-Host '' +Write-Host 'Verificando o que é necessário...' -ForegroundColor Gray +$ffmpeg = Get-Command ffmpeg -ErrorAction SilentlyContinue +if (-not $ffmpeg) { Write-Host ''; Write-Host '[ERRO] FFmpeg não encontrado.' -ForegroundColor Red; Write-Host 'Instale com: winget install Gyan.FFmpeg' -ForegroundColor White; Pausar; exit 1 } +Write-Host "[OK] FFmpeg: $($ffmpeg.Source)" -ForegroundColor Green + +Titulo 'O que você quer fazer?' +Write-Host ' [1] Vídeo -> GIF' -ForegroundColor White +Write-Host ' [2] Fotos (pasta) -> vídeo (slideshow)' -ForegroundColor White +$acao = $null +while (-not $acao) { $a = Read-Host 'Opção (1-2)'; switch ($a.Trim()) { '1'{$acao='gif'} '2'{$acao='slide'} default{Write-Host '[!] Digite 1 ou 2.' -ForegroundColor Yellow} } } + +if ($acao -eq 'gif') { + # ---------------- Vídeo -> GIF ---------------- + $entrada = $null; $modoArquivo = $false; $arquivoUnico = $null + while (-not $entrada) { + Titulo 'Pasta ou vídeo' + $p = (Read-Host 'Cole o caminho de um VÍDEO ou de uma PASTA').Trim().Trim('"') + if (Test-Path -LiteralPath $p -PathType Leaf) { + if ($extVideo -notcontains [System.IO.Path]::GetExtension($p).ToLowerInvariant()) { Write-Host '[!] Não é um vídeo suportado.' -ForegroundColor Yellow; continue } + $entrada = (Resolve-Path -LiteralPath $p).Path; $modoArquivo = $true; $arquivoUnico = $entrada + } elseif (Test-Path -LiteralPath $p -PathType Container) { $entrada = (Resolve-Path -LiteralPath $p).Path } + else { Write-Host '[!] Caminho não encontrado.' -ForegroundColor Yellow } + } + $lEnt = Read-Host 'Largura do GIF em px (Enter = 480)'; $larg = if ($lEnt -as [int]) { [int]$lEnt } else { 480 } + $fEnt = Read-Host 'Quadros por segundo / FPS (Enter = 12)'; $fps = if ($fEnt -as [int]) { [int]$fEnt } else { 12 } + + if ($modoArquivo) { $arquivoObj = Get-Item -LiteralPath $arquivoUnico; $origem = $arquivoObj.DirectoryName; $videos = @($arquivoObj) } + else { $origem = $entrada; $videos = @(Get-ChildItem -LiteralPath $origem -File | Where-Object { $extVideo -contains $_.Extension.ToLowerInvariant() }) } + $destino = Join-Path $origem 'GIF' + + Titulo 'Processando' + Write-Host "Largura: $larg px | FPS: $fps | Destino: $destino" + Write-Host "Vídeos: $($videos.Count)" -ForegroundColor Cyan + if ($videos.Count -eq 0) { Write-Host 'Nenhum vídeo encontrado.' -ForegroundColor Yellow; Pausar; exit 0 } + New-Item -ItemType Directory -Path $destino -Force | Out-Null + + $ok=0;$err=0;$i=0 + foreach ($v in $videos) { + $i++ + $saida = Join-Path $destino ($v.BaseName + '.gif') + Write-Host ''; Write-Host ("[{0}/{1}] {2}" -f $i,$videos.Count,$v.Name) -ForegroundColor Magenta + if (Test-Path -LiteralPath $saida) { Write-Host ' [IGNORADO] Já existe.' -ForegroundColor Yellow; continue } + $paleta = Join-Path $env:TEMP ("paleta_{0}.png" -f ([guid]::NewGuid().ToString('N'))) + $filtro = "fps=$fps,scale=${larg}:-1:flags=lanczos" + & ffmpeg -hide_banner -nostdin -i $v.FullName -vf "$filtro,palettegen" -y $paleta 2>$null + & ffmpeg -hide_banner -nostdin -i $v.FullName -i $paleta -lavfi "$filtro [x]; [x][1:v] paletteuse" -y $saida 2>$null + Remove-Item -LiteralPath $paleta -Force -ErrorAction SilentlyContinue + if ($LASTEXITCODE -eq 0 -and (Test-Path -LiteralPath $saida)) { Write-Host ' [OK] GIF criado.' -ForegroundColor Green; $ok++ } + else { Write-Host ' [ERRO] Falha.' -ForegroundColor Red; if (Test-Path -LiteralPath $saida) { Remove-Item -LiteralPath $saida -Force -ErrorAction SilentlyContinue }; $err++ } + } + Titulo 'Concluído' + Write-Host "GIFs criados: $ok" -ForegroundColor Green + Write-Host "Erros: $err" -ForegroundColor $(if ($err -gt 0){'Red'}else{'Gray'}) + Write-Host "Destino: $destino" + Pausar; exit 0 +} + +# ---------------- Fotos -> vídeo (slideshow) ---------------- +$pasta = $null +while (-not $pasta) { + Titulo 'Pasta com as fotos' + $p = (Read-Host 'Cole o caminho da pasta').Trim().Trim('"') + if (Test-Path -LiteralPath $p -PathType Container) { $pasta = (Resolve-Path -LiteralPath $p).Path } else { Write-Host '[!] Pasta não encontrada.' -ForegroundColor Yellow } +} +$fotos = @(Get-ChildItem -LiteralPath $pasta -File | Where-Object { $extImg -contains $_.Extension.ToLowerInvariant() } | Sort-Object Name) +if ($fotos.Count -eq 0) { Write-Host 'Nenhuma imagem nessa pasta.' -ForegroundColor Yellow; Pausar; exit 0 } +Write-Host ''; Write-Host "$($fotos.Count) fotos encontradas (em ordem alfabética)." -ForegroundColor Cyan + +$sEnt = Read-Host 'Segundos por foto (Enter = 2)'; $seg = if ($sEnt -as [double]) { [double]$sEnt } else { 2 } +$nome = (Read-Host 'Nome do vídeo de saída (Enter = slideshow.mp4)').Trim() +if ([string]::IsNullOrWhiteSpace($nome)) { $nome = 'slideshow.mp4' } +if ($nome -notmatch '\.mp4$') { $nome += '.mp4' } +$saida = Join-Path $pasta $nome + +# Monta arquivo de lista para o demuxer concat (caminhos com / e aspas) +$listaTxt = Join-Path $env:TEMP ("slide_{0}.txt" -f ([guid]::NewGuid().ToString('N'))) +$sb = New-Object System.Text.StringBuilder +foreach ($f in $fotos) { + $cam = $f.FullName -replace '\\','/' + [void]$sb.AppendLine("file '$cam'") + [void]$sb.AppendLine("duration $seg") +} +# repete a última foto (exigência do concat p/ respeitar a última duração) +$ultimo = $fotos[-1].FullName -replace '\\','/' +[void]$sb.AppendLine("file '$ultimo'") +[System.IO.File]::WriteAllText($listaTxt, $sb.ToString(), (New-Object System.Text.UTF8Encoding($false))) + +Titulo 'Gerando slideshow' +Write-Host "Saída: $saida" -ForegroundColor Cyan +$vf = 'scale=1280:720:force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2:color=black,format=yuv420p' +& ffmpeg -hide_banner -nostdin -f concat -safe 0 -i $listaTxt -vf $vf -r 30 -c:v libx264 -preset medium -crf 20 -movflags +faststart -y $saida 2>$null +$rc = $LASTEXITCODE +Remove-Item -LiteralPath $listaTxt -Force -ErrorAction SilentlyContinue + +Titulo 'Concluído' +if ($rc -eq 0 -and (Test-Path -LiteralPath $saida)) { + Write-Host "[OK] Slideshow criado com $($fotos.Count) fotos." -ForegroundColor Green + Write-Host "Arquivo: $saida" +} else { Write-Host '[ERRO] Falha ao gerar o slideshow.' -ForegroundColor Red } +Pausar
A
Vídeos/extrair-audio.cmd
@@ -0,0 +1,115 @@
+<# : +@echo off +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Extrair Áudio - tira o áudio de vídeos (FFmpeg) +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = 'Extrair Áudio' } catch {} + +function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() } +function Titulo($t) { + Write-Host '' + Write-Host '============================================================' -ForegroundColor DarkCyan + Write-Host " $t" -ForegroundColor Cyan + Write-Host '============================================================' -ForegroundColor DarkCyan +} + +$configDir = Join-Path $env:APPDATA 'ExtrairAudio' +$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($e,$f) { try { New-Item -ItemType Directory -Path $configDir -Force | Out-Null; [pscustomobject]@{ UltimaEntrada=$e; UltimoFormato=$f } | ConvertTo-Json | Set-Content -LiteralPath $configFile -Encoding UTF8 } catch {} } + +$extensoes = @('.mp4','.mkv','.avi','.mov','.wmv','.flv','.webm','.m4v','.mpg','.mpeg','.mts','.m2ts','.ts','.3gp','.vob') + +$formatos = @( + [pscustomobject]@{ Id=1; Nome='MP3'; Desc='Compatível com tudo (192 kbps)'; Ext='.mp3'; Args=@('-c:a','libmp3lame','-b:a','192k') }, + [pscustomobject]@{ Id=2; Nome='M4A (AAC)'; Desc='Ótima qualidade x tamanho (192 kbps)'; Ext='.m4a'; Args=@('-c:a','aac','-b:a','192k') }, + [pscustomobject]@{ Id=3; Nome='WAV'; Desc='Sem compressão (arquivo grande)'; Ext='.wav'; Args=@('-c:a','pcm_s16le') } +) + +Titulo 'Extrair Áudio' +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray +Write-Host '' +Write-Host 'Verificando o que é necessário...' -ForegroundColor Gray +$ffmpeg = Get-Command ffmpeg -ErrorAction SilentlyContinue +if (-not $ffmpeg) { + Write-Host ''; Write-Host '[ERRO] FFmpeg não encontrado.' -ForegroundColor Red + Write-Host 'Instale com: winget install Gyan.FFmpeg' -ForegroundColor White + Pausar; exit 1 +} +Write-Host "[OK] FFmpeg: $($ffmpeg.Source)" -ForegroundColor Green +$config = Carregar-Config + +# Pasta ou vídeo +$entradaAtual = if ($config -and $config.UltimaEntrada) { $config.UltimaEntrada } else { Join-Path $env:USERPROFILE 'Downloads\Vídeos' } +$modoArquivo = $false; $arquivoUnico = $null +while ($true) { + Titulo 'Pasta ou vídeo' + Write-Host " $entradaAtual" -ForegroundColor White + Write-Host 'Enter = confirmar | ou cole uma PASTA ou um VÍDEO' -ForegroundColor DarkGray + $resp = Read-Host 'Pasta/vídeo' + if (-not [string]::IsNullOrWhiteSpace($resp)) { $entradaAtual = $resp.Trim().Trim('"') } + if (Test-Path -LiteralPath $entradaAtual -PathType Leaf) { + if ($extensoes -notcontains [System.IO.Path]::GetExtension($entradaAtual).ToLowerInvariant()) { Write-Host '[!] Não é um vídeo suportado.' -ForegroundColor Yellow; continue } + $modoArquivo = $true; $arquivoUnico = (Resolve-Path -LiteralPath $entradaAtual).Path; break + } elseif (Test-Path -LiteralPath $entradaAtual -PathType Container) { $modoArquivo = $false; break } + Write-Host "[!] Caminho não encontrado." -ForegroundColor Yellow +} +if ($modoArquivo) { $arquivoObj = Get-Item -LiteralPath $arquivoUnico; $origem = $arquivoObj.DirectoryName } else { $origem = (Resolve-Path -LiteralPath $entradaAtual).Path } +$destino = Join-Path $origem 'Áudio' + +# Formato +$fmtPadrao = if ($config -and $config.UltimoFormato) { [int]$config.UltimoFormato } else { 1 } +Titulo 'Formato do áudio' +foreach ($f in $formatos) { + $m = if ($f.Id -eq $fmtPadrao) { '>' } else { ' ' } + $c = if ($f.Id -eq $fmtPadrao) { 'White' } else { 'Gray' } + Write-Host ("{0} [{1}] {2}" -f $m,$f.Id,$f.Nome) -ForegroundColor $c + Write-Host (" {0}" -f $f.Desc) -ForegroundColor DarkGray +} +Write-Host "Enter = padrão [$fmtPadrao]" -ForegroundColor DarkGray +$formato = $null +while (-not $formato) { + $e = Read-Host 'Formato (1-3)'; if ([string]::IsNullOrWhiteSpace($e)) { $e = "$fmtPadrao" } + $formato = $formatos | Where-Object { $_.Id -eq ($e -as [int]) } | Select-Object -First 1 + if (-not $formato) { Write-Host '[!] Inválido.' -ForegroundColor Yellow } +} +Salvar-Config $origem $formato.Id + +# Coleta +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 "Formato: $($formato.Nome) | Destino: $destino" +Write-Host "Vídeos encontrados: $($arquivos.Count)" -ForegroundColor Cyan +if ($arquivos.Count -eq 0) { Write-Host 'Nenhum vídeo encontrado.' -ForegroundColor Yellow; Pausar; exit 0 } +New-Item -ItemType Directory -Path $destino -Force | Out-Null + +$ok=0;$ign=0;$err=0;$i=0 +foreach ($a in $arquivos) { + $i++ + $rel = $a.FullName.Substring($origem.Length).TrimStart('\'); $sub = Split-Path $rel -Parent + $pastaSaida = if ([string]::IsNullOrWhiteSpace($sub)) { $destino } else { Join-Path $destino $sub } + New-Item -ItemType Directory -Path $pastaSaida -Force | Out-Null + $saida = Join-Path $pastaSaida ($a.BaseName + $formato.Ext) + Write-Host ''; Write-Host ("[{0}/{1}] {2}" -f $i,$arquivos.Count,$a.Name) -ForegroundColor Magenta + if (Test-Path -LiteralPath $saida) { Write-Host ' [IGNORADO] Já existe.' -ForegroundColor Yellow; $ign++; continue } + $ffArgs = @('-hide_banner','-nostdin','-i',$a.FullName,'-vn') + $formato.Args + @('-y',$saida) + & ffmpeg @ffArgs + if ($LASTEXITCODE -eq 0 -and (Test-Path -LiteralPath $saida)) { Write-Host ' [OK] Áudio extraído.' -ForegroundColor Green; $ok++ } + else { Write-Host ' [ERRO] Falha (o vídeo tem áudio?).' -ForegroundColor Red; if (Test-Path -LiteralPath $saida) { Remove-Item -LiteralPath $saida -Force -ErrorAction SilentlyContinue }; $err++ } +} +Titulo 'Concluído' +Write-Host "Extraídos: $ok" -ForegroundColor Green +Write-Host "Ignorados: $ign" -ForegroundColor Yellow +Write-Host "Erros: $err" -ForegroundColor $(if ($err -gt 0){'Red'}else{'Gray'}) +Write-Host "Destino: $destino" +Pausar
M
Vídeos/transcodificar.cmd
→
Vídeos/transcodificar.cmd
@@ -68,6 +68,8 @@ # ------------------------------------------------------------
# 1) Verificação de dependências # ------------------------------------------------------------ Titulo 'Transcodificar Vídeos' +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray +Write-Host '' Write-Host 'Verificando o que é necessário...' -ForegroundColor Gray $ffmpeg = Get-Command ffmpeg -ErrorAction SilentlyContinue
A
docs/achar-duplicados.md
@@ -0,0 +1,23 @@
+# Achar duplicados + +Programa que encontra arquivos iguais pelo conteúdo (não só pelo nome) dentro de uma pasta e subpastas. + +## Como abrir + +Abra o arquivo `achar-duplicados.cmd` na pasta `Organizar` (duplo clique ou Execute). + +## Passo a passo + +1. Cole o caminho da pasta a analisar. +2. Aguarde a análise (pastas grandes podem demorar). +3. Se houver duplicados, veja o resumo: grupos de iguais, cópias extras e espaço que dá para recuperar. Até 15 grupos são listados na tela; o primeiro de cada grupo é marcado como manter. +4. Escolha o que fazer: + - **Só listar** (Enter = padrão) — nada é alterado. + - **Mover as cópias extras** para a subpasta `_Duplicados`. + - **Apagar as cópias extras** — pede confirmação digitando `APAGAR`; sempre mantém 1 original. + +## Dicas + +- Pastas com muitos arquivos iguais de tamanho passam por uma comparação mais profunda — paciência. +- A opção de apagar é permanente; se tiver dúvida, use só listar ou mover. +- Pressione Enter no final para fechar a janela.
A
docs/converter-imagens.md
@@ -0,0 +1,45 @@
+# Converter Imagens + +Programa para redimensionar e/ou converter imagens, uma de cada vez ou várias de uma pasta. + +## Como abrir + +Abra o arquivo `converter-imagens.cmd` na pasta `Imagens` (duplo clique ou Execute). + +Na primeira vez, se faltar algo necessário no computador, o programa avisa e mostra o que instalar. Depois de instalar, feche e abra de novo. + +## Passo a passo + +1. **Pasta ou imagem** + Confirme o caminho sugerido (Enter) ou cole/digite o caminho de uma pasta ou de uma imagem. + Se a pasta não existir, o programa pergunta se quer criá-la. + +2. **Formato de saída** + Escolha o número da opção: + - manter o formato original (só redimensiona) + - JPG + - PNG + - WebP + + Enter usa a opção marcada como padrão (a última que você usou, quando houver). + +3. **Tamanho** + Escolha: + - pixels do maior lado (ex.: 1920) + - porcentagem (ex.: 50) + - não redimensionar (só muda o formato) + + Depois informe o valor, se for o caso. Enter pode repetir o valor usado da última vez. + +4. **Aguarde o processamento** + O programa lista cada imagem e mostra se foi convertida, ignorada (já existia no destino) ou se deu erro. + +5. **Resultado** + Os arquivos ficam na pasta `Convertido`, dentro da pasta de origem (ou ao lado da imagem única). A estrutura de subpastas é preservada. + +## Dicas + +- Você pode processar uma pasta inteira (incluindo subpastas) ou só um arquivo. +- Formatos comuns de entrada são aceitos (fotos e imagens do dia a dia). +- O programa lembra pasta, formato e tamanho usados nas últimas execuções. +- Pressione Enter no final para fechar a janela.
A
docs/criar-gif.md
@@ -0,0 +1,35 @@
+# Criar GIF / Slideshow + +Programa para transformar vídeo em GIF, ou uma pasta de fotos em um vídeo (slideshow). + +## Como abrir + +Abra o arquivo `criar-gif.cmd` na pasta `Vídeos` (duplo clique ou Execute). + +Na primeira vez, se faltar algo necessário no computador, o programa avisa e mostra o que instalar. Depois de instalar, feche e abra de novo. + +## O que escolher + +1. **Vídeo → GIF** +2. **Fotos (pasta) → vídeo (slideshow)** + +--- + +### Vídeo → GIF + +1. Cole o caminho de um vídeo ou de uma pasta com vídeos. +2. Informe a largura do GIF em pixels (Enter = 480). +3. Informe os quadros por segundo (Enter = 12). +4. Os GIFs ficam na pasta `GIF`, ao lado dos vídeos. Arquivos que já existem são ignorados. + +### Fotos → slideshow + +1. Cole o caminho da pasta com as fotos. +2. Informe quantos segundos cada foto aparece (Enter = 2). +3. Digite o nome do vídeo (Enter = `slideshow.mp4`). +4. O arquivo é criado **dentro da mesma pasta**. A ordem das fotos é alfabética. + +## Dicas + +- GIFs muito longos ou com muita resolução ficam grandes; use largura e FPS menores se precisar. +- Pressione Enter no final para fechar a janela.
A
docs/extrair-audio.md
@@ -0,0 +1,34 @@
+# Extrair Áudio + +Programa para tirar o áudio de vídeos, um de cada vez ou vários de uma pasta. + +## Como abrir + +Abra o arquivo `extrair-audio.cmd` na pasta `Vídeos` (duplo clique ou Execute). + +Na primeira vez, se faltar algo necessário no computador, o programa avisa e mostra o que instalar. Depois de instalar, feche e abra de novo. + +## Passo a passo + +1. **Pasta ou vídeo** + Confirme o caminho sugerido (Enter) ou cole/digite uma pasta ou um arquivo de vídeo. + +2. **Formato do áudio** + Escolha: + - MP3 — compatível com tudo + - M4A — bom equilíbrio qualidade × tamanho + - WAV — sem compressão (arquivo maior) + + Enter usa o formato padrão (o último usado, quando houver). + +3. **Aguarde o processamento** + Cada vídeo é listado; arquivos que já existem no destino são ignorados. + +4. **Resultado** + Os áudios ficam na pasta `Áudio`, dentro da pasta de origem. Subpastas são preservadas. + +## Dicas + +- O programa lembra a última pasta e o formato usados. +- Se um vídeo não tiver faixa de áudio, a extração pode falhar. +- Pressione Enter no final para fechar a janela.
A
docs/imagens-e-pdf.md
@@ -0,0 +1,35 @@
+# Imagens e PDF + +Programa para juntar imagens em um PDF, ou transformar páginas de PDF em imagens. + +## Como abrir + +Abra o arquivo `imagens-e-pdf.cmd` na pasta `Imagens` (duplo clique ou Execute). + +Na primeira vez, se faltar algo necessário no computador, o programa avisa e mostra o que instalar. Depois de instalar, feche e abra de novo. + +## O que escolher + +1. **Imagens → PDF** — junta as imagens de uma pasta em um único PDF. +2. **PDF → Imagens** — salva cada página do PDF como imagem. + +--- + +### Imagens → PDF + +1. Cole o caminho da pasta com as imagens. +2. Confira quantas imagens foram encontradas (ordem alfabética). +3. Digite o nome do PDF (Enter = `saida.pdf`). +4. O arquivo é criado **dentro da mesma pasta**. + +### PDF → Imagens + +1. Cole o caminho de um PDF ou de uma pasta com PDFs. +2. Escolha o formato: PNG (Enter = padrão) ou JPG. +3. Informe a qualidade em DPI (Enter = 150; 300 = alta). +4. As imagens vão para uma pasta com o nome do PDF + `-imagens` (ex.: `documento-imagens`). + +## Dicas + +- Nas imagens → PDF, a ordem das páginas segue o nome dos arquivos (A–Z). +- Pressione Enter no final para fechar a janela.
A
docs/limpeza-segura.md
@@ -0,0 +1,21 @@
+# Limpeza segura + +Programa que limpa arquivos temporários do sistema e, se quiser, esvazia a Lixeira — sempre com prévia antes de apagar. + +## Como abrir + +Abra o arquivo `limpeza-segura.cmd` na pasta `Úteis` (duplo clique ou Execute). + +## Passo a passo + +1. Aguarde a análise. Nada é apagado ainda. +2. Veja a prévia: temporários do usuário, do Windows e cache de atualizações, com tamanho aproximado e total recuperável. +3. Confirme com `s` (ou `sim`) para limpar esses temporários. Qualquer outra resposta pula essa etapa. Arquivos em uso são pulados automaticamente. +4. Em seguida, o programa pergunta se quer **esvaziar a Lixeira** também (`s`/`sim` ou não). +5. Ao terminar, veja quanto espaço foi liberado (quando a limpeza rodou). + +## Dicas + +- A limpeza é só de pastas temporárias/cache listadas — não mexe em Documentos, Downloads, etc. +- Esvaziar a Lixeira é permanente; confirme só se tiver certeza. +- Pressione Enter no final para fechar a janela.
A
docs/marca-dagua.md
@@ -0,0 +1,32 @@
+# Marca d'água + +Programa para aplicar texto ou logo em imagens, uma de cada vez ou várias de uma pasta. + +## Como abrir + +Abra o arquivo `marca-dagua.cmd` na pasta `Imagens` (duplo clique ou Execute). + +Na primeira vez, se faltar algo necessário no computador, o programa avisa e mostra o que instalar. Depois de instalar, feche e abra de novo. + +## Passo a passo + +1. **Pasta ou imagem** + Confirme o caminho sugerido (Enter) ou cole/digite uma pasta ou uma imagem. + +2. **Tipo de marca** + - **Texto** — digite o texto que aparecerá na foto. + - **Logo** — informe o caminho de um PNG (de preferência com fundo transparente) e, se quiser, o tamanho em % da largura (Enter = 20). + +3. **Posição** + Escolha: inferior direita (padrão), inferior esquerda, superior direita, superior esquerda ou centro. + +4. **Aguarde o processamento** + Cada imagem é listada; as que já existem no destino são ignoradas. + +5. **Resultado** + As fotos ficam na pasta `ComMarca`, dentro da pasta de origem. Subpastas são preservadas. + +## Dicas + +- O programa lembra a última pasta e opções usadas. +- Pressione Enter no final para fechar a janela.
A
docs/organizar-por-tipo.md
@@ -0,0 +1,20 @@
+# Organizar por tipo + +Programa que separa os arquivos soltos de uma pasta em subpastas por categoria (imagens, vídeos, áudio, documentos, etc.). + +## Como abrir + +Abra o arquivo `organizar-por-tipo.cmd` na pasta `Organizar` (duplo clique ou Execute). + +## Passo a passo + +1. Cole o caminho da pasta a organizar. +2. Veja a prévia: quantos arquivos vão para cada categoria (Imagens, Vídeos, Áudio, Documentos, Compactados, Programas, Outros). +3. Confirme com `s` (ou `sim`) para mover. Qualquer outra resposta cancela. +4. Os arquivos são movidos para subpastas com esses nomes, **dentro da mesma pasta**. Se já existir um arquivo com o mesmo nome, um número é acrescentado. + +## Dicas + +- Só organiza arquivos **soltos** na pasta (não entra em subpastas já existentes). +- Confira a prévia antes de confirmar. +- Pressione Enter no final para fechar a janela.
A
docs/relatorio-sistema.md
@@ -0,0 +1,19 @@
+# Relatório do sistema + +Programa que mostra um resumo do computador: nome, sistema, processador, memória, vídeo, discos e espaço nas unidades. + +## Como abrir + +Abra o arquivo `relatorio-sistema.cmd` na pasta `Úteis` (duplo clique ou Execute). + +## Passo a passo + +1. O relatório aparece na tela em seções (computador, processador e memória, vídeo, discos, espaço nas unidades). +2. No final, escolha se quer **salvar num arquivo .txt** (`s`/`sim` ou não). + Se salvar, o arquivo vai para a **Área de trabalho**, com data e hora no nome. +3. Pressione Enter para fechar. + +## Dicas + +- Útil para guardar um “retrato” do PC ou enviar para alguém que vá te ajudar. +- Unidades quase cheias aparecem em destaque na tela.
A
docs/renomear-em-lote.md
@@ -0,0 +1,23 @@
+# Renomear em lote + +Programa para renomear vários arquivos de uma pasta de uma vez, ou organizá-los em pastas por data. + +## Como abrir + +Abra o arquivo `renomear-em-lote.cmd` na pasta `Organizar` (duplo clique ou Execute). + +## Passo a passo + +1. Cole o caminho da pasta (só arquivos **nessa pasta**, sem subpastas). +2. Escolha o modo: + - **Sequência** — prefixo + número (ex.: Foto_001). Informe prefixo, número inicial e quantos dígitos. + - **Data/hora** — nome com data e hora do arquivo (em fotos, tenta usar a data da foto quando disponível). + - **Localizar e substituir** — troca um trecho do nome por outro (pode deixar o substituto vazio para remover). + - **Mover para pastas por data** — cria pastas `AAAA-MM` e move os arquivos conforme a data. +3. Veja a prévia na tela e confirme com `s` (ou `sim`). Qualquer outra resposta cancela. + +## Dicas + +- Sempre há prévia antes de aplicar — confira antes de confirmar. +- Se o novo nome já existir, o programa evita conflito acrescentando um número. +- Pressione Enter no final para fechar a janela.
A
docs/transcodificar.md
@@ -0,0 +1,39 @@
+# Transcodificar + +Programa para converter vídeos, um de cada vez ou vários de uma pasta, com escolha de qualidade. + +## Como abrir + +Abra o arquivo `transcodificar.cmd` na pasta `Vídeos` (duplo clique ou Execute). + +Na primeira vez, se faltar algo necessário no computador, o programa avisa e mostra o que instalar. Depois de instalar, feche e abra de novo. + +## Passo a passo + +1. **Pasta ou vídeo** + Confirme o caminho sugerido (Enter) ou cole/digite o caminho de uma pasta ou de um arquivo de vídeo. + Se a pasta não existir, o programa pergunta se quer criá-la. + +2. **Qualidade de saída** + Escolha o número da opção: + - Máxima (resolução original) + - Alta (1080p) + - Média (720p) + - Baixa (480p) + - Mínima (compacta, para prévia ou envio rápido) + + Enter usa a opção marcada como padrão (a última que você usou; se for a primeira vez, costuma ser a mínima). + +3. **Aguarde o processamento** + O programa lista cada vídeo e mostra se foi convertido, ignorado (já existia no destino) ou se deu erro. Vídeos longos podem demorar. + +4. **Resultado** + Os arquivos ficam na pasta `Transcodificado`, dentro da pasta de origem (ou ao lado do vídeo único), em formato adequado para uso geral. A estrutura de subpastas é preservada. + +## Dicas + +- Você pode processar uma pasta inteira (incluindo subpastas) ou só um arquivo. +- Formatos comuns de vídeo são aceitos. +- Qualidade maior = arquivo maior e conversão mais lenta; qualidade menor = arquivo mais leve. +- O programa lembra a pasta e a qualidade usadas nas últimas execuções. +- Pressione Enter no final para fechar a janela.
A
docs/verificar-discos.md
@@ -0,0 +1,26 @@
+# Verificar Discos + +Programa para consultar a saúde dos HDs e SSDs do computador. Só lê informações; não apaga nem altera dados. + +## Como abrir + +Abra o arquivo `verificar-discos.cmd` na pasta `Úteis` (duplo clique ou Execute). + +O Windows pode pedir permissão de administrador — aceite para ver o máximo de informações. Sem isso, parte dos dados pode faltar. + +## Menu + +Depois de abrir, escolha uma opção pelo número: + +1. **Diagnóstico de saúde** — rápido e recomendado no dia a dia. Mostra, para cada disco, se está bem, temperatura, desgaste, horas ligado e erros, quando disponíveis. +2. **Teste de superfície** — lê o disco inteiro em busca de problemas. Pode levar horas. É seguro (só leitura). Você escolhe qual disco testar; Enter cancela. Use Ctrl+C se quiser interromper no meio. +3. **Resumo simples** — lista rápida de cada disco com OK ou aviso/falha. +0. **Sair** — fecha o programa (Enter vazio também sai). + +Após as opções 1, 2 ou 3, pressione Enter para voltar ao menu. + +## Dicas + +- Comece pelo diagnóstico de saúde (opção 1); use o teste de superfície só se quiser uma verificação mais profunda. +- O teste de superfície demora muito em discos grandes — deixe o PC ligado e evite desligar no meio. +- Se algum disco aparecer com aviso ou falha, considere fazer backup e procurar assistência.
A
Úteis/limpeza-segura.cmd
@@ -0,0 +1,101 @@
+<# : +@echo off +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Limpeza segura - remove temporários e esvazia a lixeira +# Mostra uma prévia do que será apagado antes de fazer +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = 'Limpeza segura' } catch {} + +function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() } +function Titulo($t) { + Write-Host '' + Write-Host '============================================================' -ForegroundColor DarkCyan + Write-Host " $t" -ForegroundColor Cyan + Write-Host '============================================================' -ForegroundColor DarkCyan +} +function Tam($bytes) { + if ($bytes -ge 1GB) { '{0:N2} GB' -f ($bytes/1GB) } + elseif ($bytes -ge 1MB) { '{0:N1} MB' -f ($bytes/1MB) } + else { '{0:N0} KB' -f ($bytes/1KB) } +} +function Medir($caminho) { + if (-not (Test-Path -LiteralPath $caminho)) { return [pscustomobject]@{ Itens=0; Bytes=0 } } + $itens = @(Get-ChildItem -LiteralPath $caminho -Recurse -Force -ErrorAction SilentlyContinue) + $bytes = ($itens | Where-Object { -not $_.PSIsContainer } | Measure-Object -Property Length -Sum).Sum + return [pscustomobject]@{ Itens=$itens.Count; Bytes=[int64]$bytes } +} + +# Locais seguros de limpeza (apenas pastas de temporários/cache) +$alvos = @( + [pscustomobject]@{ Nome='Temporários do usuário'; Caminho=$env:TEMP }, + [pscustomobject]@{ Nome='Temporários do Windows'; Caminho='C:\Windows\Temp' }, + [pscustomobject]@{ Nome='Cache do Windows Update (Download)'; Caminho='C:\Windows\SoftwareDistribution\Download' } +) + +Titulo 'Limpeza segura' +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray +Write-Host '' +Write-Host 'Analisando o que pode ser limpo... (nada é apagado ainda)' -ForegroundColor Gray + +$totalBytes = 0 +foreach ($alvo in $alvos) { + $m = Medir $alvo.Caminho + $alvo | Add-Member -NotePropertyName Bytes -NotePropertyValue $m.Bytes -Force + $alvo | Add-Member -NotePropertyName Itens -NotePropertyValue $m.Itens -Force + $totalBytes += $m.Bytes +} + +Titulo 'Prévia (o que será limpo)' +foreach ($alvo in $alvos) { + $cor = if ($alvo.Bytes -gt 0) { 'White' } else { 'DarkGray' } + Write-Host (" {0,-38} {1,10} ({2} itens)" -f $alvo.Nome, (Tam $alvo.Bytes), $alvo.Itens) -ForegroundColor $cor + Write-Host (" {0}" -f $alvo.Caminho) -ForegroundColor DarkGray +} +Write-Host '' +Write-Host ("Total recuperável (aprox.): {0}" -f (Tam $totalBytes)) -ForegroundColor Cyan +Write-Host '(Arquivos em uso são pulados automaticamente.)' -ForegroundColor DarkGray + +Write-Host '' +$ok = Read-Host 'Limpar os temporários listados acima? (s/N)' +if ($ok -match '^(s|sim|y|yes)$') { + Titulo 'Limpando' + foreach ($alvo in $alvos) { + if (-not (Test-Path -LiteralPath $alvo.Caminho)) { continue } + Write-Host (" {0}..." -f $alvo.Nome) -ForegroundColor Gray + Get-ChildItem -LiteralPath $alvo.Caminho -Force -ErrorAction SilentlyContinue | ForEach-Object { + Remove-Item -LiteralPath $_.FullName -Recurse -Force -ErrorAction SilentlyContinue + } + } + # Recalcula o que sobrou p/ estimar o que foi liberado + $sobrou = 0 + foreach ($alvo in $alvos) { $sobrou += (Medir $alvo.Caminho).Bytes } + $liberado = $totalBytes - $sobrou + if ($liberado -lt 0) { $liberado = 0 } + Write-Host '' + Write-Host ("[OK] Liberado aproximadamente: {0}" -f (Tam $liberado)) -ForegroundColor Green +} else { + Write-Host 'Temporários não foram alterados.' -ForegroundColor Yellow +} + +Write-Host '' +$lix = Read-Host 'Esvaziar a Lixeira também? (s/N)' +if ($lix -match '^(s|sim|y|yes)$') { + try { + Clear-RecycleBin -Force -ErrorAction Stop + Write-Host '[OK] Lixeira esvaziada.' -ForegroundColor Green + } catch { + if ("$($_.Exception.Message)" -match 'empty|vazia|0x8000FFFF') { Write-Host '[OK] A Lixeira já estava vazia.' -ForegroundColor Green } + else { Write-Host "[!] Não foi possível esvaziar a Lixeira: $($_.Exception.Message)" -ForegroundColor Yellow } + } +} + +Titulo 'Concluído' +Write-Host 'Limpeza finalizada.' -ForegroundColor Green +Pausar
A
Úteis/relatorio-sistema.cmd
@@ -0,0 +1,87 @@
+<# : +@echo off +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText('%~f0',[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Relatório do sistema - specs, memória, discos e espaço +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = 'Relatório do sistema' } catch {} + +function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() } +function GB($bytes) { '{0:N1} GB' -f ($bytes/1GB) } + +$linhas = New-Object System.Collections.Generic.List[string] +function Add($txt, $cor='Gray') { Write-Host $txt -ForegroundColor $cor; $linhas.Add($txt) } +function Sec($t) { + Write-Host '' + Write-Host "== $t ==" -ForegroundColor Cyan + $linhas.Add(''); $linhas.Add("== $t ==") +} + +Write-Host '' +Write-Host '============================================================' -ForegroundColor DarkCyan +Write-Host ' Relatório do sistema' -ForegroundColor Cyan +Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray +Write-Host '============================================================' -ForegroundColor DarkCyan +$linhas.Add('=== Relatório do sistema - Desenvolvido por Pablo Murad - 2026 ===') +$linhas.Add("Gerado em: $(Get-Date -Format 'dd/MM/yyyy HH:mm')") + +try { + $os = Get-CimInstance Win32_OperatingSystem + $cs = Get-CimInstance Win32_ComputerSystem + $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 + + Sec 'Computador' + Add ("Nome: {0}" -f $env:COMPUTERNAME) + Add ("Usuário: {0}" -f $env:USERNAME) + Add ("Fabricante:{0} {1}" -f $cs.Manufacturer, $cs.Model) + Add ("Sistema: {0}" -f $os.Caption) + Add ("Versão: {0} (build {1})" -f $os.Version, $os.BuildNumber) + $boot = $os.LastBootUpTime + $up = (Get-Date) - $boot + Add ("Ligado há: {0}d {1}h {2}min (desde {3:dd/MM HH:mm})" -f $up.Days, $up.Hours, $up.Minutes, $boot) + + Sec 'Processador e memória' + Add ("CPU: {0}" -f $cpu.Name.Trim()) + Add ("Núcleos: {0} físicos / {1} lógicos" -f $cpu.NumberOfCores, $cpu.NumberOfLogicalProcessors) + $ramTotal = $os.TotalVisibleMemorySize * 1KB + $ramLivre = $os.FreePhysicalMemory * 1KB + $ramUso = $ramTotal - $ramLivre + $pct = if ($ramTotal -gt 0) { [int](($ramUso / $ramTotal) * 100) } else { 0 } + Add ("Memória: {0} no total | {1} em uso ({2}%) | {3} livre" -f (GB $ramTotal), (GB $ramUso), $pct, (GB $ramLivre)) + + Sec 'Vídeo' + Get-CimInstance Win32_VideoController | ForEach-Object { Add ("GPU: {0}" -f $_.Name) } + + Sec 'Discos físicos' + Get-PhysicalDisk | Sort-Object DeviceId | ForEach-Object { + $tam = if ($_.Size -ge 1TB) { '{0:N2} TB' -f ($_.Size/1TB) } else { GB $_.Size } + $cor = if ($_.HealthStatus -eq 'Healthy') { 'Green' } elseif ($_.HealthStatus -eq 'Warning') { 'Yellow' } else { 'Red' } + Add ("[{0}] {1,-26} {2,10} {3} ({4})" -f $_.DeviceId, $_.FriendlyName, $tam, $_.HealthStatus, $_.MediaType) $cor + } + + Sec 'Espaço nas unidades' + Get-Volume | Where-Object { $_.DriveLetter -and $_.Size -gt 0 } | Sort-Object DriveLetter | ForEach-Object { + $usado = $_.Size - $_.SizeRemaining + $pctU = if ($_.Size -gt 0) { [int](($usado / $_.Size) * 100) } else { 0 } + $rot = if ($_.FileSystemLabel) { $_.FileSystemLabel } else { '(sem rótulo)' } + $cor = if ($pctU -ge 90) { 'Red' } elseif ($pctU -ge 75) { 'Yellow' } else { 'Gray' } + Add ("{0}: {1,-18} {2} livres de {3} ({4}% usado)" -f $_.DriveLetter, $rot, (GB $_.SizeRemaining), (GB $_.Size), $pctU) $cor + } +} catch { + Write-Host "[ERRO] $($_.Exception.Message)" -ForegroundColor Red +} + +Write-Host '' +$salvar = Read-Host 'Salvar este relatório num arquivo .txt? (s/N)' +if ($salvar -match '^(s|sim|y|yes)$') { + $arq = Join-Path ([Environment]::GetFolderPath('Desktop')) ("relatorio-sistema_{0}.txt" -f (Get-Date -Format 'yyyy-MM-dd_HH-mm')) + try { $linhas | Set-Content -LiteralPath $arq -Encoding UTF8; Write-Host "[OK] Salvo em: $arq" -ForegroundColor Green } + catch { Write-Host "[ERRO] Não foi possível salvar: $($_.Exception.Message)" -ForegroundColor Red } +} +Pausar
A
Úteis/verificar-discos.cmd
@@ -0,0 +1,222 @@
+<# : +@echo off +set "SELF=%~f0" +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "Invoke-Expression ([System.IO.File]::ReadAllText($env:SELF,[System.Text.Encoding]::UTF8))" +exit /b %errorlevel% +#> + +# ============================================================ +# Verificar Discos - saúde dos HDs/SSDs (somente leitura) +# Desenvolvido por Pablo Murad - 2026 +# ============================================================ + +try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {} +try { $Host.UI.RawUI.WindowTitle = 'Verificar Discos' } catch {} + +# ------------------------------------------------------------ +# Auto-elevação para administrador (necessário p/ ler tudo) +# ------------------------------------------------------------ +$souAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent() + ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +if (-not $souAdmin) { + Write-Host 'Solicitando privilégios de administrador...' -ForegroundColor Yellow + try { + Start-Process -FilePath 'cmd.exe' -ArgumentList '/c', "`"$env:SELF`"" -Verb RunAs | Out-Null + } catch { + Write-Host '[!] Sem elevação. Alguns dados (temperatura, testes) podem faltar.' -ForegroundColor Yellow + Start-Sleep -Seconds 2 + } + exit +} + +function Pausar { + Write-Host '' + Write-Host 'Pressione Enter para voltar ao menu...' -ForegroundColor DarkGray + [void][System.Console]::ReadLine() +} + +function Titulo($texto) { + Write-Host '' + Write-Host '============================================================' -ForegroundColor DarkCyan + Write-Host " $texto" -ForegroundColor Cyan + Write-Host '============================================================' -ForegroundColor DarkCyan +} + +function Cor-Saude($status) { + switch ("$status") { + 'Healthy' { 'Green' } + 'Warning' { 'Yellow' } + default { if ([string]::IsNullOrWhiteSpace("$status")) { 'DarkGray' } else { 'Red' } } + } +} + +function Formatar-Tamanho($bytes) { + if (-not $bytes) { return '-' } + $gb = [double]$bytes / 1GB + if ($gb -ge 1024) { return ('{0:N2} TB' -f ($gb / 1024)) } + return ('{0:N0} GB' -f $gb) +} + +$smartctl = Get-Command smartctl -ErrorAction SilentlyContinue + +# ------------------------------------------------------------ +# Ação 1: Diagnóstico de saúde (rápido, somente leitura) +# ------------------------------------------------------------ +function Diagnostico-Saude { + Titulo 'Diagnóstico de saúde' + + $discos = Get-PhysicalDisk | Sort-Object DeviceId + foreach ($d in $discos) { + $cor = Cor-Saude $d.HealthStatus + Write-Host '' + Write-Host ("Disco {0}: {1}" -f $d.DeviceId, $d.FriendlyName) -ForegroundColor White + Write-Host (" Tipo: {0} | Tamanho: {1} | Barramento: {2}" -f ` + $d.MediaType, (Formatar-Tamanho $d.Size), $d.BusType) -ForegroundColor Gray + Write-Host (" Saúde: {0} (operacional: {1})" -f $d.HealthStatus, ($d.OperationalStatus -join ',')) -ForegroundColor $cor + + # Contadores de confiabilidade (temperatura, desgaste, erros) + try { + $rc = $d | Get-StorageReliabilityCounter -ErrorAction Stop + $partes = @() + if ($null -ne $rc.Temperature) { $partes += "Temp: $($rc.Temperature) C" } + if ($null -ne $rc.Wear) { $partes += "Desgaste: $($rc.Wear)%" } + if ($null -ne $rc.PowerOnHours) { $partes += "Horas ligado: $($rc.PowerOnHours)" } + if ($null -ne $rc.ReadErrorsTotal) { $partes += "Erros leitura: $($rc.ReadErrorsTotal)" } + if ($null -ne $rc.WriteErrorsTotal) { $partes += "Erros escrita: $($rc.WriteErrorsTotal)" } + if ($partes.Count -gt 0) { Write-Host (" " + ($partes -join ' | ')) -ForegroundColor DarkGray } + } catch {} + + # SMART detalhado via smartctl, se disponível + if ($smartctl) { + try { + $dev = "/dev/pd$($d.DeviceId)" + $saidaS = & smartctl -H -A -d ata $dev 2>$null + if (-not $saidaS) { $saidaS = & smartctl -H -A $dev 2>$null } + $linhaSaude = $saidaS | Select-String 'SMART overall-health|test result' + if ($linhaSaude) { Write-Host (" SMART: " + ($linhaSaude[0].Line.Trim())) -ForegroundColor Gray } + $attrs = $saidaS | Select-String 'Reallocated_Sector|Current_Pending|Offline_Uncorrectable|Power_On_Hours' + foreach ($a in $attrs) { Write-Host (" " + ($a.Line.Trim() -replace '\s{2,}', ' ')) -ForegroundColor DarkGray } + } catch {} + } + } + + if (-not $smartctl) { + Write-Host '' + Write-Host '(Dica: para detalhes SMART avançados instale o smartmontools:' -ForegroundColor DarkGray + Write-Host ' winget install smartmontools )' -ForegroundColor DarkGray + } +} + +# ------------------------------------------------------------ +# Ação 2: Resumo simples +# ------------------------------------------------------------ +function Resumo-Simples { + Titulo 'Resumo simples' + Get-PhysicalDisk | Sort-Object DeviceId | ForEach-Object { + $ok = $_.HealthStatus -eq 'Healthy' + $txt = if ($ok) { 'OK' } else { "FALHA/AVISO ($($_.HealthStatus))" } + $cor = Cor-Saude $_.HealthStatus + Write-Host ('[{0}] {1,-32} {2,10} ' -f $_.DeviceId, $_.FriendlyName, (Formatar-Tamanho $_.Size)) -NoNewline + Write-Host $txt -ForegroundColor $cor + } +} + +# ------------------------------------------------------------ +# Ação 3: Teste de superfície (leitura sequencial, só leitura) +# ------------------------------------------------------------ +function Teste-Superficie { + Titulo 'Teste de superfície (somente leitura)' + $discos = Get-PhysicalDisk | Sort-Object DeviceId + foreach ($d in $discos) { + Write-Host (' [{0}] {1} ({2})' -f $d.DeviceId, $d.FriendlyName, (Formatar-Tamanho $d.Size)) -ForegroundColor Gray + } + Write-Host '' + Write-Host 'AVISO: lê todos os setores do disco escolhido. Pode levar HORAS.' -ForegroundColor Yellow + Write-Host 'É seguro (apenas leitura). Ctrl+C cancela a qualquer momento.' -ForegroundColor DarkGray + $sel = Read-Host 'Número do disco para testar (Enter = cancelar)' + if ([string]::IsNullOrWhiteSpace($sel)) { Write-Host 'Cancelado.' -ForegroundColor Gray; return } + + $alvo = $discos | Where-Object { "$($_.DeviceId)" -eq $sel.Trim() } | Select-Object -First 1 + if (-not $alvo) { Write-Host '[!] Disco não encontrado.' -ForegroundColor Yellow; return } + + $total = [int64]$alvo.Size + $caminho = "\\.\PhysicalDrive$($alvo.DeviceId)" + Write-Host '' + Write-Host "Lendo $caminho ($(Formatar-Tamanho $total))..." -ForegroundColor Cyan + + $bloco = 8MB + $buffer = New-Object byte[] $bloco + $lidos = [int64]0 + $errosRegioes = @() + $inicio = Get-Date + $ultimoPct = -1 + + try { + $fs = [System.IO.File]::Open($caminho, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) + } catch { + Write-Host "[ERRO] Não foi possível abrir o disco: $($_.Exception.Message)" -ForegroundColor Red + return + } + + try { + while ($lidos -lt $total) { + try { + $n = $fs.Read($buffer, 0, $bloco) + if ($n -le 0) { break } + $lidos += $n + } catch { + $errosRegioes += ('offset {0:N0} MB' -f ($lidos / 1MB)) + # pula o bloco problemático e continua + $lidos += $bloco + try { $fs.Seek($lidos, [System.IO.SeekOrigin]::Begin) | Out-Null } catch { break } + } + + $pct = [int](($lidos / $total) * 100) + if ($pct -ne $ultimoPct) { + $ultimoPct = $pct + $decorrido = (Get-Date) - $inicio + $mbs = if ($decorrido.TotalSeconds -gt 0) { ($lidos / 1MB) / $decorrido.TotalSeconds } else { 0 } + Write-Host ("`r Progresso: {0,3}% ({1:N0}/{2:N0} MB) {3:N0} MB/s Erros: {4} " -f ` + $pct, ($lidos / 1MB), ($total / 1MB), $mbs, $errosRegioes.Count) -NoNewline -ForegroundColor Cyan + } + } + } finally { + $fs.Close() + } + + Write-Host '' + Write-Host '' + if ($errosRegioes.Count -eq 0) { + Write-Host "[OK] Nenhum erro de leitura. Todos os setores lidos com sucesso." -ForegroundColor Green + } else { + Write-Host ("[!] {0} regiões com erro de leitura (possíveis setores defeituosos):" -f $errosRegioes.Count) -ForegroundColor Red + $errosRegioes | Select-Object -First 20 | ForEach-Object { Write-Host " $_" -ForegroundColor Red } + if ($errosRegioes.Count -gt 20) { Write-Host " ... e mais $($errosRegioes.Count - 20)." -ForegroundColor Red } + } +} + +# ------------------------------------------------------------ +# Menu principal +# ------------------------------------------------------------ +while ($true) { + Titulo 'Verificar Discos' + Write-Host ' Desenvolvido por Pablo Murad - 2026' -ForegroundColor DarkGray + Write-Host '' + Write-Host ' [1] Diagnóstico de saúde (rápido, recomendado)' -ForegroundColor White + Write-Host ' SMART, temperatura, erros e desgaste de todos os discos' -ForegroundColor DarkGray + Write-Host ' [2] Teste de superfície (lê todos os setores - demorado)' -ForegroundColor White + Write-Host ' Procura setores defeituosos num disco escolhido' -ForegroundColor DarkGray + Write-Host ' [3] Resumo simples (só OK/Falha por disco)' -ForegroundColor White + Write-Host ' [0] Sair' -ForegroundColor White + Write-Host '' + $op = Read-Host 'Escolha (0-3)' + + switch ($op.Trim()) { + '1' { Diagnostico-Saude; Pausar } + '2' { Teste-Superficie; Pausar } + '3' { Resumo-Simples; Pausar } + '0' { break } + '' { break } + default { Write-Host '[!] Opção inválida.' -ForegroundColor Yellow; Start-Sleep -Seconds 1 } + } +}