@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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.
@@ -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
@@ -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
@@ -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 }
+ }
+}