17 Commits

Author SHA1 Message Date
PrincessPi 586e8746be media conversion powershell added 2026-09-25 17:34:01 -06:00
PrincessPi 9bfedfafc5 CHECKSUM GREATLY IMPROVED 2026-09-25 17:15:48 -06:00
PrincessPi 4e798818d1 CHECKSUM GREATLY IMPROVED 2026-09-25 17:02:59 -06:00
PrincessPi 56b08f32da waypoit debuguing 2026-09-25 16:52:30 -06:00
PrincessPi 251c9aa06c waypoit debuguing 2026-09-25 16:51:04 -06:00
PrincessPi 1dbd1ad79b waypoit debuguing 2026-09-25 16:50:16 -06:00
PrincessPi 7bd65e7d76 waypoit debuguing 2026-09-25 16:45:30 -06:00
PrincessPi 2fd8238a98 waypoit debuguing 2026-09-25 16:33:58 -06:00
PrincessPi 503e947eaf waypoit debuguing 2026-09-25 16:20:22 -06:00
PrincessPi 07a29167dd waypoit debuguing 2026-09-25 16:19:14 -06:00
PrincessPi 608e225833 WORKAN DEBUG 2026-09-25 16:10:17 -06:00
PrincessPi 4fea9dbccc waypoint debug 2026-09-25 16:01:49 -06:00
PrincessPi 4377cc6a45 waypoit debuguing 2026-09-25 15:56:47 -06:00
PrincessPi e5435d124e waypoit debuguing 2026-09-25 15:50:28 -06:00
PrincessPi 757f1775ff waypoit debuguing 2026-09-25 15:43:25 -06:00
PrincessPi dc17c77c87 waypoint debug 2026-09-25 15:30:52 -06:00
PrincessPi b504218205 waypoint debug 2026-09-25 15:27:37 -06:00
3 changed files with 652 additions and 268 deletions
+9
View File
@@ -0,0 +1,9 @@
1. recursive verify error: `FAILED open or read` RIGHT NOW GENERATES RELATIVE PATH/FILENAME IN CHECKSUMS FILES IN SUBDIRS, SHOULD JUST BE FOR THE FILES IN DAT DIR (for linux compatability)
2. recursive progress set to count total number of files recursively first for progress bar.
3. paralell processing
4. Princess Pi ascii art magenta
5. princess pi bubble text ascii art blue
6. script for full top-to-bottom testing
7. BASH linux version
8. more hashing algos?
9. multi-algo paralell workin? like multiple -a params?
+473 -183
View File
@@ -1,192 +1,311 @@
<#
.SYNOPSIS
Princess Pi's Magical CHECKSUM! - Computes and verifies message digests / file checksums.
.DESCRIPTION
Computes or verifies checksums using cryptographic hash algorithms (SHA256, SHA512, SHA1, MD5, SHA384).
Supports parallel processing, recursive scanning, direct hash matching, and verification file parsing.
.NOTES
Requires PowerShell 7.0 or higher.
#>
[CmdletBinding(DefaultParameterSetName='Generate')] [CmdletBinding(DefaultParameterSetName='Generate')]
param( param(
# File(s) or wildcard pattern to compute checksum for # File(s) or wildcard pattern to compute checksum for
[Parameter(Position = 0, ParameterSetName = 'Generate', ValueFromPipeline = $true)] [Parameter(Position = 0, ParameterSetName = 'Generate', ValueFromPipeline = $true)]
[Parameter(Position = 0, ParameterSetName = 'Match', ValueFromPipeline = $true)] [Parameter(Position = 0, ParameterSetName = 'Match', ValueFromPipeline = $true)]
[Parameter(Position = 0, ParameterSetName = 'PrincessPi', ValueFromPipeline = $false)]
[Alias('i', 'input', 'if', 'InputFile')]
[string[]]$FilePath, [string[]]$FilePath,
# Output file switch (-o) - auto generates file based on rules # Custom output file (-f / -OutFile / -of)
[Parameter(Mandatory = $false, ParameterSetName = 'Generate')] [Parameter(Mandatory = $false, ParameterSetName = 'Generate')]
[Alias('o', 'out')] [Alias('f', 'of')]
[switch]$SaveToFile,
# Custom Output file name (-f)
[Parameter(Mandatory = $false, ParameterSetName = 'Generate')]
[Alias('f', 'outfile')]
[string]$OutFile, [string]$OutFile,
# File containing checksums to verify (-c mode) # Auto output file switch (-s / -Save / -o)
[Parameter(Mandatory = $false, ParameterSetName = 'Generate')]
[Alias('s', 'o')]
[switch]$Save,
# File containing checksums to verify (-c / -Check)
[Parameter(Mandatory = $false, ParameterSetName = 'Verify')] [Parameter(Mandatory = $false, ParameterSetName = 'Verify')]
[Alias('c', 'check')] [Alias('c')]
[string]$Check, [string]$Check,
# Direct checksum hash string to match against (-m mode) # Direct checksum hash string to match against (-m / -Match)
[Parameter(Mandatory = $true, ParameterSetName = 'Match')] [Parameter(Mandatory = $true, ParameterSetName = 'Match')]
[Alias('m', 'match')] [Alias('m')]
[string]$Match, [string]$Match,
# Hashing algorithm to use (Defaults to SHA256) # Hashing algorithm to use (Defaults to SHA256)
[Parameter(Mandatory = $false)] [Parameter(Mandatory = $false)]
[Alias('a', 'algo', 'algorithm')] [Alias('a', 'algo')]
[ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5')] [ValidateSet('SHA1', 'SHA256', 'SHA384', 'SHA512', 'MD5')]
[string]$Algorithm, [string]$Algorithm,
# Recursive flag (-r) # Recursive flag (-r / -Recursive)
[Parameter(Mandatory = $false)] [Parameter(Mandatory = $false)]
[Alias('r', 'recursive')] [Alias('r')]
[switch]$Recursive, [switch]$Recursive,
# Help flag (-h) # Princess Pi art flag (-pp / -PrincessPi)
[Parameter(Mandatory = $true, ParameterSetName = 'PrincessPi')]
[Alias('pp')]
[switch]$PrincessPi,
# Help flag (-h / -Help)
[Parameter(Mandatory = $false)] [Parameter(Mandatory = $false)]
[Alias('h', 'help')] [Alias('h')]
[switch]$Help [switch]$Help
) )
begin { # --- ENVIRONMENT SANITY CHECK ---
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Error "checksum: This script requires PowerShell 7 or higher for parallel execution (-Parallel support). Current version is $($PSVersionTable.PSVersion)."
exit 1
}
# --- HELPER FUNCTIONS ---
function Show-Help { function Show-Help {
@' @'
Princess Pi's Magical CHECKSUM!
NAME NAME
checksum - Compute and verify message digests / file checksums checksum - Compute and verify message digests / file checksums
SYNOPSIS SYNOPSIS
.\checksum.ps1 [-FilePath] <string[]> [-o] [-OutFile <string>] [-Algorithm <string>] [-r] .\checksum.ps1 [-FilePath] <string[]> [-s] [-f <string>] [-Algorithm <string>] [-r]
.\checksum.ps1 [-FilePath] <string[]> -Match <string> [-Algorithm <string>] [-r] .\checksum.ps1 [-FilePath] <string[]> -Match <string> [-Algorithm <string>] [-r]
.\checksum.ps1 -Check <string> [-Algorithm <string>] [-r] .\checksum.ps1 -Check <string> [-Algorithm <string>] [-r]
.\checksum.ps1 -PrincessPi
.\checksum.ps1 -Help .\checksum.ps1 -Help
DESCRIPTION DESCRIPTION
Computes or verifies checksums using various cryptographic algorithms. Computes or verifies checksums using cryptographic hash algorithms (SHA256, SHA512, SHA1, MD5, SHA384).
Outputs hash values in standard Linux utility format (<hash> <filename>). Outputs hash values in standard GNU coreutils format (<hash> <filename>).
Ignores checksum files (.md5, .sha1, .sha256, .sha384, .sha512) unless explicitly passed. Ignores checksum metadata files (.md5, .sha1, .sha256, .sha384, .sha512) during wildcards unless passed explicitly.
PARAMETERS PARAMETERS
-FilePath <string[]> -FilePath, -i, -input, -if, -InputFile <string[]>
Specifies the path to one or more files to hash. Accepts wildcards (*). Specifies the target file(s) or wildcard pattern (*).
-o, -Out, --Out <switch> -s, -S, -Save, -o, -O <switch>
Enables writing checksum results to an auto-named file. Auto-saves checksum results. (Takes no arguments).
- If input has a wildcard (*), outputs to 'checksums.<algo>' - If input filename contains a wildcard (*), saves to 'checksums.<algo>'.
- If no wildcard is used, outputs to '<filename>.<algo>' per file. - If input filename does NOT contain a wildcard, saves to '<filename>.<algo>'.
When combined with -r and *, it auto-generates checksum files When combined with -r and *, recursively auto-generates checksums per directory.
inside the current working directory and each subdirectory recursively.
-f, -OutFile, --OutFile <string> -f, -F, -of, -OF, -OutFile <string>
Specifies an explicit output file name to save results to. Saves checksum results to a specific custom filename.
Overrides the auto-naming behavior of -s if both are passed.
-r, -Recursive, --Recursive <switch> -c, -C, -Check <string>
Recursively generate or verify checksums in subdirectories. Reads hashes from a specified checksum file and verifies target files on disk.
If -r is specified, recursively looks for and validates checksum files by name across subdirectories.
-c, -Check, --Check <string> -m, -M, -Match <string>
Read checksums from the specified file and verify them against files on disk. Compares the computed hash of target file(s) directly against a given hash string.
If -r is used, searches for the specified filename recursively in subdirectories.
-m, -Match, --Match <string> -a, -A, -Algo, -Algorithm <string>
Compare the computed hash of the target file(s) against a specific hash string. Specifies the hash algorithm to use.
Supported options: MD5, SHA1, SHA256 (default), SHA384, SHA512.
In -c or -m mode, auto-detects algorithm from hash length if omitted.
-a, -Algorithm, --Algorithm <string> -r, -R, -Recursive <switch>
Specifies the cryptographic hash algorithm to use. Processes target files or verification files recursively across directory structures.
Supported values: SHA1, SHA256 (default), SHA384, SHA512, MD5.
If omitted in -c or -m mode, the algorithm is auto-detected by hash length.
-h, -Help, --Help <switch> -pp, -PP, -PrincessPi <switch>
Display this help message. Displays Princess Pi!
-h, -H, -Help <switch>
Displays this help documentation.
Also displays on any error.
EXAMPLES EXAMPLES
1. Compute SHA256 checksum for a single file: 1. Compute SHA256 for a single file and auto-save as 'myfile.iso.sha256':
.\checksum.ps1 myfile.iso .\checksum.ps1 myfile.iso -s
2. Compute checksums for all files and auto-write to files (-o flag): 2. Compute SHA512 recursively and auto-save as 'checksums.sha512' in each folder:
.\checksum.ps1 * -r -o .\checksum.ps1 * -r -s -a SHA512
3. Compute SHA256 checksums and save to a specific custom file:
.\checksum.ps1 * -f my_hashes.txt
4. Verify checksums against a verification file:
.\checksum.ps1 -c checksums.sha256
5. Compare file directly against an expected hash string:
.\checksum.ps1 myfile.bin -m 406653a68bd3538975656831f33f031b
Princess Pi LOVES YOU!~
https://linktr.ee/princesspi3
3. Verify checksums from verification files recursively:
.\checksum.ps1 -c checksums.sha256 -r
'@ '@
} }
function Write-ErrorWithHelp {
param([string]$ErrorMessage);
Show-Help;
Write-Host "";
Write-Error $ErrorMessage;
}
function Show-PrincessPiArt {
$art = @'
=#= -- ##*+++++ *+
-#=#- +# -*%%= ##++++++++++# ##*********#
#==#= *** %%++%% -++# #++++++++++++*+ *#***************#
#===#= %***+%++#*%%@+++++% #++++++++++++**+++#******++********#
====+#%@*******###%=%++##++% #+++++++++++*++++++++#***====+****++#
=#=*==**#*#########%++#*+++@ *++++++++#+++++++++++#****==+******#
*#=====%####***%%++++#*+++**% ++++++++*+++++++++++++#************#*******
@****#==%####**#*+++++++#+++#**# #+++++++++++++++++++#####+*****************#
@****#######%**@+*%**#%+++++%##*** #+++++++++++++++++#+++++#****+======*****+
#****##%####@**%+*******@@+++@###**% #+++++++++++++*++++++++++****========****#
****#####%%*%*%+***@@@%%@*+++@####** *+++++++++++++++++++++#****=======****#
%***####%%**+%%*++*@=##@@%@++++%###*** ++++*+++++++++++#+**************#
@%%%@@##%%#++++++++==**@@**+++%*###**% +++++++++++#*+++++#*********####
%###%++++++++====**#++++%**##**% @% ++++#+++++++++++++**************#
%###%++++++++++++++++++%**%##**%@###@ %++++++++++++++++***=====********
###%%++++++++%++++++*%%%%###***##%##@ +++++++++++++##**************#
###%%%%+++++##+++++++#*+%###***%%%##@ ++++++++++++++%********+############%@
@%%*@*%%%%###****+*%%****++%##***%##@@@ #+++++++++++++++*********###*****#########*
#%%###***#####%**++++##**%#@@#@ ##+++++++++*###%#+***+***##*************#######
@#%%####%%####%+++++++##*######%++++++++++++++#*+++++#%###%#######*********######%
##%########@%@+++@###%*%#####@+++++++++++%**++++++++++%%%%%%########***==**###%##%
%#%###%###########@@%#%#@@##++++++++****++++********+++ %########**===*###%*#
#**##%####@@####%@######%*++++++++++++++++#++**+*#++++# ########******###% %
@#***# ###%@@##%#######@+++++++++++++++++++++%*++**++++# *########******##% @
*******% %##% =@#########++++++++++++++++++#++++*%++*#%#++# ########******##%
#@%@# %% *@#@@###*+++++++++++++++++++%+++##+++###++* ##*#####******##@
**##%*++%++++++++++++++++++++++++++++++++% +#**#####******###
##%#######*@***+#+++++++++++++++#***#++++++++++++ %**#####*******#%
*+++++$****##****++++++++##% #******#+++++++++++ %**#####*******#@
#++++++++*******- ++++++++# *******#++++++++++# @**#####*******#
#@@+++++++******- *+++++++# #******#+++++++++* **####%**==+**@
@@@@@+++ %+++++++# ******# %+++++++ %*###%***===**@ %=
@@@@@@@@@ #+++++++# =****** #++++++% *###%#***=+**% %**
@@@@@@@@@@ ++++++++# ******# #++++++# *##@%********% @*@
@@+@@@++@@+ ++++++++# ******# #++++++# @## #*********@ #***#
@@+@@++@@@++ @@@++++++* @@@%**** #@@++++% +## %#****************%
@*@**@+++# @@@@+++++ @@@@@@**# @@@@@++++# @# %#****************
+#+++++# @@@@@@+++% @@@@@@@@# @@@@@@@++ @ %##************%
#+++** @@@@@@@@@@ @@@@@@@@@ @@@@@@@@@ %###********%*
@@@@@@@@@@+ @@@@@@@@@@ =@@@@@@@@@ %%#####%%
*@@@@@@@@@@ @+*@@@@@@@= @@++@@@@@@
@++@@@@@@@@ #**@**@@@@@ *@@*#@@@@@@ -=
@%#@@@@@@@@ *#***@@@@@@ @+#+@++#@@@
#+++@++@@@@@ **********# #+++++@+@@@@
##+#+++@@@@@@ ##****#### *+#+##+++@@%%
#+++++++++++# #++++++++++#
#########*
____ ____ ____ _ _ ___ ____ ___ ___ ____ ____
( _ \( _ \(_ _)( \( )/ __)( ___)/ __)/ __) ( _ \(_ _)/\/
)___/ ) / _)(_ ) (( (__ )__) \__ \\__ \ )___/ _)(_
(__) (_)\_)(____)(_)\_)\___)(____)(___/(___/ (__) (____)
Princess Pi
https://linktr.ee/princesspi3
'@
Write-Host $art -ForegroundColor Blue;
}
function Get-AlgorithmFromHashLength { function Get-AlgorithmFromHashLength {
param([string]$HashString) param([string]$HashString);
switch ($HashString.Trim().Length) { switch ($HashString.Trim().Length) {
32 { return 'MD5' } 32 { return 'MD5'; }
40 { return 'SHA1' } 40 { return 'SHA1'; }
64 { return 'SHA256' } 64 { return 'SHA256'; }
96 { return 'SHA384' } 96 { return 'SHA384'; }
128 { return 'SHA512' } 128 { return 'SHA512'; }
default { default {
Write-Error "checksum: Could not auto-detect algorithm for hash length $($HashString.Length). Please specify -Algorithm." Write-ErrorWithHelp "checksum: Could not auto-detect algorithm for hash length $($HashString.Length). Please specify -Algorithm.";
return $null return $null;
}
} }
} }
} }
process { # --- MAIN EXECUTION FLOW ---
# Bulletproof help check (catches --help passed positionally) $helpInPath = $false;
$helpInPath =$false
if ($FilePath) { if ($FilePath) {
foreach ($p in ($FilePath)) { foreach ($p in ($FilePath)) {
if ($p -match '^(?i)[-/]{1,2}(h\vert{}help)$') { if ($p -match '^(?i)[-/]{1,2}(h|help|\?)$') {
$helpInPath =$true $helpInPath = $true;
break break;
} }
} }
} }
if ($Help.IsPresent -or $helpInPath) { if ($Help.IsPresent -or $helpInPath) {
Show-Help Show-Help;
return return;
}
if ($PrincessPi.IsPresent) {
Show-PrincessPiArt;
return;
} }
if (-not [string]::IsNullOrWhiteSpace($Algorithm)) { if (-not [string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm =$Algorithm.ToUpper() $Algorithm = $Algorithm.ToUpper();
} }
$isWildcardGlobal = $false;
if ($FilePath) {
foreach ($p in $FilePath) {
if ($p -match '\*') { $isWildcardGlobal = $true; }
}
}
$showProgress = $Recursive.IsPresent -or $isWildcardGlobal -or ($PSCmdlet.ParameterSetName -eq 'Verify');
# --- ITEM GATHERING (Generate & Match Modes) --- # --- ITEM GATHERING (Generate & Match Modes) ---
if ($PSCmdlet.ParameterSetName -in @('Generate', 'Match')) { if ($PSCmdlet.ParameterSetName -in @('Generate', 'Match')) {
if (-not $FilePath -or $FilePath.Count -eq 0) { if (-not $FilePath -or $FilePath.Count -eq 0) {
Write-Error "checksum: Missing file path parameter. Use -Help for usage." Write-ErrorWithHelp "checksum: Missing file path parameter. Use -Help for usage.";
return return;
} }
$allItems = @(); $allItems = @();
$isWildcardGlobal =$false;
$skipExtensions = @('.md5', '.sha1', '.sha256', '.sha384', '.sha512'); $skipExtensions = @('.md5', '.sha1', '.sha256', '.sha384', '.sha512');
foreach ($pathEntry in ($FilePath)) { foreach ($pathEntry in ($FilePath)) {
$isWildcard = ($pathEntry -match '\*'); $isWildcard = ($pathEntry -match '\*');
if ($isWildcard) { $isWildcardGlobal =$true }
$resolvedItems = @(); $resolvedItems = @();
if ($Recursive) { if ($Recursive) {
# Parens around variables prevent space-stripping crashes $resolvedItems = Get-ChildItem -Path ($pathEntry) -Recurse -File -ErrorAction 'SilentlyContinue';
$resolvedItems = Get-ChildItem -Path ($pathEntry) -Recurse -File -ErrorAction 'SilentlyContinue'
} else { } else {
if ($isWildcard -or ($pathEntry -match '\?')) { if ($isWildcard -or ($pathEntry -match '\?')) {
$resolvedItems = Get-ChildItem -Path ($pathEntry) -File -ErrorAction 'SilentlyContinue' $resolvedItems = Get-ChildItem -Path ($pathEntry) -File -ErrorAction 'SilentlyContinue';
} else { } else {
$resolvedItems = Get-Item -Path ($pathEntry) -ErrorAction 'SilentlyContinue' $resolvedItems = Get-Item -Path ($pathEntry) -ErrorAction 'SilentlyContinue';
} }
} }
if (-not $resolvedItems) { if (-not $resolvedItems) {
Write-Error "checksum: $($pathEntry)`: No such file or directory" Write-ErrorWithHelp "checksum: $($pathEntry): No such file or directory";
continue continue;
} }
$explicitFileFound = $false; $explicitFileFound = $false;
if ((-not $isWildcard) -and (Test-Path -Path ($pathEntry) -PathType 'Leaf')) { if ((-not $isWildcard) -and (Test-Path -Path ($pathEntry) -PathType 'Leaf')) {
$explicitFileFound = $true $explicitFileFound = $true;
} }
foreach ($item in ($resolvedItems)) { foreach ($item in ($resolvedItems)) {
if ($item.PSIsContainer) { continue } if ($item.PSIsContainer) { continue; }
if (-not $explicitFileFound) { if (-not $explicitFileFound) {
if ($skipExtensions -contains $item.Extension.ToLower()) { if ($skipExtensions -contains $item.Extension.ToLower()) {
continue continue;
} }
} }
$allItems += $item $allItems += $item;
} }
} }
} }
@@ -194,173 +313,344 @@ process {
# --- DIRECT MATCH MODE (-m / -Match) --- # --- DIRECT MATCH MODE (-m / -Match) ---
if ($PSCmdlet.ParameterSetName -eq 'Match') { if ($PSCmdlet.ParameterSetName -eq 'Match') {
if ([string]::IsNullOrWhiteSpace($Algorithm)) { if ([string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm = Get-AlgorithmFromHashLength -HashString ($Match) $Algorithm = Get-AlgorithmFromHashLength -HashString ($Match);
if (-not $Algorithm) { return } if (-not $Algorithm) { return; }
} }
$expectedHash = $Match.Trim().ToLower(); $expectedHash = $Match.Trim().ToLower();
$totalFiles = $allItems.Count;
$currentIndex = 0;
foreach ($item in ($allItems)) { $allItems | ForEach-Object -Parallel {
$actualHash = (Get-FileHash -Path ($item.FullName) -Algorithm ($Algorithm)).Hash.ToLower() $item = $_
$targetFile = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue' $algo = $using:Algorithm
if (-not $targetFile) { $targetFile = $item.Name } $exp = $using:expectedHash
if ($targetFile.StartsWith(".\")) { $targetFile = $targetFile.Substring(2) }
if ($actualHash -eq $expectedHash) { $actualHash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
Write-Host "${targetFile}: " -NoNewline $targetFile = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
Write-Host "OK" -ForegroundColor Green if (-not $targetFile) { $targetFile = $item.Name; }
} else { if ($targetFile.StartsWith(".\") -or $targetFile.StartsWith("./")) { $targetFile = $targetFile.Substring(2); }
Write-Host "${targetFile}: " -NoNewline
Write-Host "FAILED" -ForegroundColor Red [PSCustomObject]@{
TargetFile = $targetFile
IsMatch = ($actualHash -eq $exp)
} }
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Matching Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
if ($_.IsMatch) {
Write-Host "$($_.TargetFile): " -NoNewline;
Write-Host "OK" -ForegroundColor Green;
} else {
Write-Host "$($_.TargetFile): " -NoNewline;
Write-Host "FAILED" -ForegroundColor Red;
}
}
if ($showProgress) {
Write-Progress -Activity "Matching Checksums ($Algorithm)" -Completed;
} }
} }
# --- VERIFICATION MODE (-c / -Check) --- # --- VERIFICATION MODE (-c / -Check) ---
elseif ($PSCmdlet.ParameterSetName -eq 'Verify') { elseif ($PSCmdlet.ParameterSetName -eq 'Verify') {
$checkFiles = @(); $checkFiles = @();
if ($Recursive) { if ($Recursive) {
$checkFileName = Split-Path -Path ($Check) -Leaf $checkFileName = Split-Path -Path ($Check) -Leaf;
$checkFiles = Get-ChildItem -Path '.\' -Filter ($checkFileName) -Recurse -File -ErrorAction 'SilentlyContinue' $checkFiles = Get-ChildItem -Path '.\' -Filter ($checkFileName) -Recurse -File -ErrorAction 'SilentlyContinue';
} else { } else {
if (Test-Path -Path ($Check) -PathType 'Leaf') { if (Test-Path -Path ($Check) -PathType 'Leaf') {
$checkFiles = @(Get-Item -Path ($Check)) $checkFiles = @(Get-Item -Path ($Check));
} }
} }
if (-not $checkFiles -or $checkFiles.Count -eq 0) { if (-not $checkFiles -or $checkFiles.Count -eq 0) {
Write-Error "checksum: $($Check)`: No such file or directory" Write-ErrorWithHelp "checksum: $($Check): No such file or directory";
return return;
} }
foreach ($cf in ($checkFiles)) { foreach ($cf in ($checkFiles)) {
$lines = Get-Content -Path ($cf.FullName) $lines = Get-Content -Path ($cf.FullName);
$baseDir =$cf.DirectoryName $baseDir = $cf.DirectoryName;
$fileAlgo =$Algorithm $fileAlgo = $Algorithm;
if ([string]::IsNullOrWhiteSpace($fileAlgo)) { if ([string]::IsNullOrWhiteSpace($fileAlgo)) {
foreach ($line in ($lines)) { foreach ($line in ($lines)) {
if ([string]::IsNullOrWhiteSpace($line) -or$line.StartsWith("#")) { continue } if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) { continue; }
if ($line -match '^([a-fA-F0-9]+)\s+') { if ($line -match '^([a-fA-F0-9]+)\s+') {
$fileAlgo = Get-AlgorithmFromHashLength -HashString ($Matches[1]) $fileAlgo = Get-AlgorithmFromHashLength -HashString ($Matches[1]);
break break;
}
elseif ($line -match '^([a-fA-F0-9]{128}|[a-fA-F0-9]{96}|[a-fA-F0-9]{64}|[a-fA-F0-9]{40}|[a-fA-F0-9]{32})') {
$fileAlgo = Get-AlgorithmFromHashLength -HashString ($Matches[1]);
break;
} }
} }
if ([string]::IsNullOrWhiteSpace($fileAlgo)) {$fileAlgo = 'SHA256' } if ([string]::IsNullOrWhiteSpace($fileAlgo)) { $fileAlgo = 'SHA256'; }
} }
$verifyItems = @();
foreach ($line in ($lines)) { foreach ($line in ($lines)) {
if ([string]::IsNullOrWhiteSpace($line) -or$line.StartsWith("#")) { continue } if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) { continue; }
if ($line -match '^([a-fA-F0-9]+)\s+[* ]?(.*)$') {$expectedHash = $Matches[1].ToLower()$targetFile = $Matches[2].Trim().TrimStart('*')$targetFullPath = Join-Path -Path ($baseDir) -ChildPath ($targetFile) $expectedHash = $null;
$targetFile = $null;
if (Test-Path -Path ($targetFullPath) -PathType 'Leaf') {$actualHash = (Get-FileHash -Path ($targetFullPath) -Algorithm ($fileAlgo)).Hash.ToLower() if ($line -match '^([a-fA-F0-9]+)\s+[* ]?(.*)$') {
$expectedHash = $Matches[1].ToLower();
$targetFile = $Matches[2].Trim().TrimStart('*');
}
elseif ($line -match '^([a-fA-F0-9]{128}|[a-fA-F0-9]{96}|[a-fA-F0-9]{64}|[a-fA-F0-9]{40}|[a-fA-F0-9]{32})[* ]?(.*)$') {
$expectedHash = $Matches[1].ToLower();
$targetFile = $Matches[2].Trim().TrimStart('*');
}
$displayPath = Resolve-Path -Path ($targetFullPath) -Relative -ErrorAction 'SilentlyContinue' if ($expectedHash -and $targetFile) {
if (-not $displayPath) { $displayPath =$targetFile } $targetFullPath = Join-Path -Path ($baseDir) -ChildPath ($targetFile);
if ($displayPath.StartsWith(".\")) { $displayPath =$displayPath.Substring(2) } $verifyItems += [PSCustomObject]@{
ExpectedHash = $expectedHash
TargetFile = $targetFile
TargetFullPath = $targetFullPath
BaseDir = $baseDir
};
}
}
if ($actualHash -eq$expectedHash) { $totalFiles = $verifyItems.Count;
Write-Host "${displayPath}: " -NoNewline $currentIndex = 0;
Write-Host "OK" -ForegroundColor Green
} else { $verifyItems | ForEach-Object -Parallel {
Write-Host "${displayPath}: " -NoNewline $item = $_
Write-Host "FAILED" -ForegroundColor Red $algo = $using:fileAlgo
if (Test-Path -Path ($item.TargetFullPath) -PathType 'Leaf') {
$actualHash = (Get-FileHash -Path ($item.TargetFullPath) -Algorithm ($algo)).Hash.ToLower();
$displayPath = Resolve-Path -Path ($item.TargetFullPath) -Relative -ErrorAction 'SilentlyContinue';
if (-not $displayPath) { $displayPath = $item.TargetFile; }
if ($displayPath.StartsWith(".\") -or $displayPath.StartsWith("./")) { $displayPath = $displayPath.Substring(2); }
[PSCustomObject]@{
DisplayPath = $displayPath
Status = if ($actualHash -eq $item.ExpectedHash) { "OK" } else { "FAILED" }
IsError = $false
} }
} else { } else {
$displayPath =$targetFile $displayPath = $item.TargetFile;
if ($baseDir -ne (Get-Location).Path) {$displayPath = Join-Path -Path (Resolve-Path -Path ($baseDir) -Relative -ErrorAction 'SilentlyContinue') -ChildPath ($targetFile) if ($item.BaseDir -ne (Get-Location).Path) {
if ($displayPath.StartsWith(".\")) { $displayPath =$displayPath.Substring(2) } $displayPath = Join-Path -Path (Resolve-Path -Path ($item.BaseDir) -Relative -ErrorAction 'SilentlyContinue') -ChildPath ($item.TargetFile);
if ($displayPath.StartsWith(".\") -or $displayPath.StartsWith("./")) { $displayPath = $displayPath.Substring(2); }
} }
Write-Host "${displayPath}: " -NoNewline [PSCustomObject]@{
Write-Host "FAILED open or read" -ForegroundColor Red DisplayPath = $displayPath
Status = "FAILED open or read"
IsError = $true
} }
} }
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++
if ($totalFiles -gt 0) {
Write-Progress -Activity "Verifying Checksums ($fileAlgo)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
} }
Write-Host "$($_.DisplayPath): " -NoNewline;
if ($_.IsError) {
Write-Host "$($_.Status)" -ForegroundColor Red;
} elseif ($_.Status -eq "OK") {
Write-Host "OK" -ForegroundColor Green;
} else {
Write-Host "FAILED" -ForegroundColor Red;
}
}
Write-Progress -Activity "Verifying Checksums ($fileAlgo)" -Completed;
} }
} }
# --- GENERATION MODE --- # --- GENERATION MODE ---
else { else {
if ([string]::IsNullOrWhiteSpace($Algorithm)) {$Algorithm = 'SHA256' if ([string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm = 'SHA256';
} }
# Enable output if the switch is used OR an explicit file is provided $wantCustomOut = $PSBoundParameters.ContainsKey('OutFile');
$wantOutFile = $SaveToFile.IsPresent -or (-not [string]::IsNullOrWhiteSpace($OutFile)); $wantAutoOut = $Save.IsPresent;
if ($wantOutFile) { if ($wantCustomOut -or $wantAutoOut) {
if ($Recursive -and $isWildcardGlobal) { if ($Recursive -and $isWildcardGlobal) {
$groupedItems =$allItems | Group-Object DirectoryName $groupedItems = $allItems | Group-Object DirectoryName;
foreach ($group in ($groupedItems)) { foreach ($group in ($groupedItems)) {
$dirPath =$group.Name $dirPath = $group.Name;
$filesInDir =$group.Group $filesInDir = $group.Group;
if ([string]::IsNullOrWhiteSpace($OutFile)) { if ($wantCustomOut) {
$currentOutFile = Join-Path -Path ($dirPath) -ChildPath "checksums.$($Algorithm.ToLower())" $currentOutFile = Join-Path -Path ($dirPath) -ChildPath ($OutFile);
} else { } else {
$currentOutFile = Join-Path -Path ($dirPath) -ChildPath ($OutFile) $currentOutFile = Join-Path -Path ($dirPath) -ChildPath "checksums.$($Algorithm.ToLower())";
} }
$null = New-Item -Path ($currentOutFile) -ItemType 'File' -Force $totalFiles = $filesInDir.Count;
$currentIndex = 0;
$linesToWrite = @();
foreach ($item in ($filesInDir)) {$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($Algorithm)).Hash.ToLower() $filesInDir | ForEach-Object -Parallel {
$relativePath =$item.Name $item = $_
$algo = $using:Algorithm
$outputLine = "$hash$relativePath" $hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
Add-Content -Path ($currentOutFile) -Value ($outputLine)
Write-Output $outputLine $relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; }
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
}
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Directory: $(Split-Path $dirPath -Leaf) [$currentIndex/$totalFiles]" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
$linesToWrite += $_.OutputLine;
Write-Output $_.OutputLine;
}
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
Set-Content -Path ($currentOutFile) -Value $linesToWrite -Encoding utf8;
}
}
else {
if ($wantCustomOut -or ($wantAutoOut -and $isWildcardGlobal)) {
if ($wantCustomOut) {
$singleOutFile = $OutFile;
} else {
$singleOutFile = "checksums.$($Algorithm.ToLower())";
}
$totalFiles = $allItems.Count;
$currentIndex = 0;
$linesToWrite = @();
$allItems | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; }
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
}
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
$linesToWrite += $_.OutputLine;
Write-Output $_.OutputLine;
}
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
Set-Content -Path ($singleOutFile) -Value $linesToWrite -Encoding utf8;
}
else {
$totalFiles = $allItems.Count;
$currentIndex = 0;
$allItems | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; }
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
}
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
Write-Output $_.OutputLine;
Set-Content -Path ($_.ItemOutFile) -Value $_.OutputLine -Encoding utf8;
}
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
} }
} }
} }
else { else {
$singleOutFile =$null; $totalFiles = $allItems.Count;
$currentIndex = 0;
if ([string]::IsNullOrWhiteSpace($OutFile)) { $allItems | ForEach-Object -Parallel {
if ($isWildcardGlobal) {$singleOutFile = "checksums.$($Algorithm.ToLower())" $item = $_
} $algo = $using:Algorithm
} else {
$singleOutFile =$OutFile $hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; }
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
} }
if ($singleOutFile) { [PSCustomObject]@{
$null = New-Item -Path ($singleOutFile) -ItemType 'File' -Force Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
} }
foreach ($item in ($allItems)) {$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($Algorithm)).Hash.ToLower() Write-Output $_.OutputLine;
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue'
if (-not $relativePath) { $relativePath =$item.Name }
if ($relativePath.StartsWith(".\")) {
$relativePath =$relativePath.Substring(2)
} }
$outputLine = "$hash$relativePath" if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
if ($singleOutFile) {
Add-Content -Path ($singleOutFile) -Value ($outputLine)
} else {
$currentOutFile = "$($item.FullName).$($Algorithm.ToLower())"
$null = New-Item -Path ($currentOutFile) -ItemType 'File' -Force
Add-Content -Path ($currentOutFile) -Value ($outputLine)
}
Write-Output $outputLine
} }
} }
} }
else {
foreach ($item in ($allItems)) {$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($Algorithm)).Hash.ToLower()
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue'
if (-not $relativePath) { $relativePath =$item.Name }
if ($relativePath.StartsWith(".\")) {
$relativePath =$relativePath.Substring(2)
}
$outputLine = "$hash$relativePath"
Write-Output $outputLine
}
}
}
}
end {}
+85
View File
@@ -0,0 +1,85 @@
param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$Ext1,
[Parameter(Mandatory = $true, Position = 1)]
[string]$Ext2,
[int]$Jobs = 4 # Number of simultaneous conversions
)
$sourceExt = $Ext1.TrimStart('.')
$targetExt = $Ext2.TrimStart('.')
$audioExts = @('opus', 'ogg', 'flac', 'wav')
$files = Get-ChildItem -File -Recurse -Filter "*.$sourceExt"
if ($files.Count -eq 0) {
Write-Host "No files matching *.$sourceExt found." -ForegroundColor Yellow
return
}
Write-Host "Starting conversion using $Jobs parallel jobs..." -ForegroundColor Green
$files | ForEach-Object -Parallel {
$file = $_
$targetExt = $using:targetExt
$audioExts = $using:audioExts
$outputPath = Join-Path -Path $file.DirectoryName -ChildPath "$($file.BaseName).$targetExt"
$relativePath = Resolve-Path -Path $file.FullName -Relative
Write-Host "Processing: $relativePath" -ForegroundColor Cyan
if ($audioExts -contains $targetExt.ToLower()) {
ffmpeg -y -i $file.FullName -c:a libopus -b:a 128k -vbr on $outputPath 2>$null
} else {
# -preset 8 drastically speeds up SVT-AV1 while maintaining high quality
ffmpeg -y -i $file.FullName -c:v libsvtav1 -crf 26 -preset 8 -c:a libopus -b:a 128k $outputPath 2>$null
}
} -ThrottleLimit $Jobs
Write-Host "Finished processing $($files.Count) file(s)." -ForegroundColor Green
# -------------------------------------------------------------------------
# Step 1: Recursively delete all existing .sha256 files
# -------------------------------------------------------------------------
Write-Host "Deleting existing .sha256 files recursively..." -ForegroundColor Yellow
Get-ChildItem -Path . -Recurse -Filter "*.sha256" -File | Remove-Item -Force
# -------------------------------------------------------------------------
# Step 2: Run checksums in current directory and every subdirectory
# -------------------------------------------------------------------------
Write-Host "Generating checksums in each subfolder..." -ForegroundColor Cyan
# Gather current working directory and all child directories
$allDirs = @((Get-Item .).FullName) + (Get-ChildItem -Path . -Recurse -Directory | Select-Object -ExpandProperty FullName)
foreach ($dir in $allDirs) {
Push-Location $dir
try {
Write-Host "Updating checksums in: $dir" -ForegroundColor Gray
checksum * -o
} finally {
Pop-Location
}
}
# -------------------------------------------------------------------------
# Step 3: Recursively verify checksums in each directory
# -------------------------------------------------------------------------
Write-Host "Verifying checksums in each subfolder..." -ForegroundColor Cyan
foreach ($dir in $allDirs) {
Push-Location $dir
try {
if (Test-Path "checksums.sha256") {
Write-Host "Verifying: $dir" -ForegroundColor Gray
checksum -c checksums.sha256
}
} finally {
Pop-Location
}
}
Write-Host "All conversions, checksum creations, and verifications completed successfully." -ForegroundColor Green