Created
February 18, 2025 09:19
-
-
Save haydenmc/6ad47340fe706f132a754e299b76d718 to your computer and use it in GitHub Desktop.
Rename FATX Files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$MAX_LENGTH = 42 | |
$files = (Get-ChildItem -File -Recurse | Where-Object { $_.Name.Length -gt $MAX_LENGTH }) | |
$validChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&''()-.@[]^_`{}~ ' | |
function Strip-Unnecessary | |
{ | |
param( | |
[string]$In | |
) | |
return ($In -replace '\[!\]','').Trim() | |
} | |
function Abbreviate-String | |
{ | |
param( | |
[Parameter(Position = 0)] | |
[string] | |
$In | |
) | |
$tokens = [regex]::Matches($In, '[^()\s]+|[()]|\s') | |
$output = "" | |
foreach($token in $tokens) | |
{ | |
if ([Char]::IsLetter($token.Value[0])) | |
{ | |
$output += [Char]::ToUpper($token.Value[0]) | |
} | |
else | |
{ | |
$output += $token.Value | |
} | |
} | |
return $output -replace '\(V\)','' # Remove useless (V) | |
} | |
# function Abbreviate-String | |
# { | |
# # Shortens each token between spaces to its first letter | |
# param( | |
# [string]$In | |
# ) | |
# $DELIMITER = ' ' | |
# $tokens = $In.Split($DELIMITER) | |
# $outputTokens = @() | |
# foreach ($token in $tokens) | |
# { | |
# if ([Char]::IsLetter($token[0])) | |
# { | |
# $outputTokens += [Char]::ToUpper($token[0]) | |
# } | |
# else | |
# { | |
# $outputTokens += $token | |
# } | |
# } | |
# return [System.String]::Join($DELIMITER, $outputTokens) | |
# } | |
function Separate-String | |
{ | |
# Attempts to split the name in a reasonable place to allow the first part to remain readable | |
param( | |
[string]$Name, | |
[int]$MaxLength | |
) | |
# Try to find a convenient place to split | |
$DEFAULT_SEPARATOR = " - " | |
# How much room to try to leave at the end for abbreviations | |
$shortenEndBuffer = [System.Math]::Min(21, $MaxLength) | |
$abbreviateAfter = ($MaxLength - $shortenEndBuffer) | |
# Try to abbreviate everything after a separating hyphen | |
$splitIndex = $Name.IndexOf($DEFAULT_SEPARATOR) | |
if (($splitIndex -lt 0) -or ($splitIndex -gt $abbreviateAfter)) | |
{ | |
# Find furthest space character that isn't greater than the end buffer | |
$spaceIndex = 0 | |
while ($true) | |
{ | |
$nextSpace = $Name.IndexOf(' ', $spaceIndex) | |
if ($nextSpace -eq -1) | |
{ | |
break | |
} | |
if (($nextSpace + 1) -gt $abbreviateAfter) | |
{ | |
break | |
} | |
$spaceIndex = $nextSpace + 1 | |
} | |
$splitIndex = $spaceIndex | |
} | |
else | |
{ | |
$splitIndex += $DEFAULT_SEPARATOR.Length | |
} | |
return @($Name.Substring(0, $splitIndex), $Name.Substring($splitIndex)) | |
} | |
function Shorten-String | |
{ | |
param( | |
[string]$Name, | |
[int]$MaxLength | |
) | |
if ($Name.Length -le $MaxLength) | |
{ | |
return $Name | |
} | |
$splitString = Separate-String -Name $Name -MaxLength $MaxLength | |
$baseName = $splitString[0] | |
$reduceName = $splitString[1] | |
Write-Debug "Strip unnecessary char sequences" | |
$reduceName = Strip-Unnecessary -In $reduceName | |
if (($baseName + $reduceName).Length -le $MaxLength) | |
{ | |
return ($baseName + $reduceName) | |
} | |
Write-Debug "Abbreviate extended name" | |
$reduceName = Abbreviate-String -In $reduceName | |
if (($baseName + $reduceName).Length -le $MaxLength) | |
{ | |
return ($baseName + $reduceName) | |
} | |
Write-Debug "Strip spaces from extended name" | |
$reduceName = $reduceName -replace " ","" | |
if (($baseName + $reduceName).Length -le $MaxLength) | |
{ | |
return ($baseName + $reduceName) | |
} | |
Write-Debug "Strip spaces from base name" | |
$baseName = $baseName -replace " ","" | |
if (($baseName + $reduceName).Length -le $MaxLength) | |
{ | |
return ($baseName + $reduceName) | |
} | |
Write-Debug "Truncate as a last resort" | |
return ($baseName + $reduceName).Substring(0, $MaxLength) | |
} | |
function Ask-Continue | |
{ | |
param ( | |
[string]$Message = "Continue? (Y/N)" | |
) | |
$response = Read-Host -Prompt $Message | |
return $response -match '^(Y|y)$' | |
} | |
$toRename = @() | |
$nameDictionary = @{} | |
$dupeNames = [System.Collections.Generic.HashSet[string]]::new() | |
foreach ($file in $files) | |
{ | |
$maxBaseLength = ($MAX_LENGTH - $file.Extension.Length) | |
$baseName = $file.BaseName | |
$newName = Shorten-String -Name $baseName -MaxLength $maxBaseLength | |
$newName += $file.Extension | |
$fileRename = @{ | |
SourceFile = $file | |
NewName = $newName | |
} | |
if (!$nameDictionary.ContainsKey($newName)) | |
{ | |
$nameDictionary[$newName] = @($fileRename) | |
} | |
else | |
{ | |
$nameDictionary[$newName] += $fileRename | |
$dupeNames.Add($newName) | Out-Null | |
} | |
$toRename += $fileRename | |
} | |
if ($dupeNames.Count -gt 0) | |
{ | |
Write-Host -ForegroundColor Yellow "Found $($dupeNames.Count) duplicates as a result of rename:" | |
foreach ($dupeName in $dupeNames) | |
{ | |
Write-Host -ForegroundColor Cyan "$($dupeName):" | |
foreach ($dupeResult in $nameDictionary[$dupeName]) | |
{ | |
Write-Host "`t-->$($dupeResult.SourceFile.FullName)" | |
} | |
} | |
Write-Host -ForegroundColor Yellow "Please resolve before operation can continue." | |
exit 1 | |
} | |
Write-Host -ForegroundColor Cyan "Will rename $($toRename.Count) files:" | |
foreach ($r in $toRename) | |
{ | |
Write-Host $r.SourceFile.FullName | |
Write-Host "`t--> ($($r.NewName.Length)) $($r.NewName)" -ForegroundColor Green | |
} | |
if (-not (Ask-Continue)) | |
{ | |
Write-Host "Abort" | |
exit 0 | |
} | |
foreach ($r in $toRename) | |
{ | |
Rename-Item -Path $r.SourceFile.FullName -NewName $r.NewName | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$invalidChars = '.*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\#\$\%\&\''\(\)\-\.\@\[\]\^_`\{\}~ ].*' | |
function Ask-Continue | |
{ | |
param ( | |
[string]$Message = "Continue? (Y/N)" | |
) | |
$response = Read-Host -Prompt $Message | |
return $response -match '^(Y|y)$' | |
} | |
$files = (Get-ChildItem -File -Recurse | Where-Object { $_.BaseName -match $invalidChars }) | |
foreach ($file in $files) | |
{ | |
# Initialize an empty string to store the cleaned filename | |
$cleanedBaseName = "" | |
# Remove invalid characters | |
foreach ($char in $file.BaseName.ToCharArray()) { | |
if ($char -notmatch $invalidChars) { | |
$cleanedBaseName += $char | |
} | |
} | |
# Quick and dirty remove duplicate spaces | |
$cleanedBaseName = $cleanedBaseName -replace " ", " " | |
$cleanedBaseName = $cleanedBaseName -replace " ", " " | |
$cleanedBaseName = $cleanedBaseName.Trim() | |
# Re-add file extension | |
$cleanedBaseName += $file.Extension | |
Write-Host -ForegroundColor Cyan $file.FullName | |
Write-Host -ForegroundColor Green "`t-->$cleanedBaseName" | |
Rename-Item -Path $file.FullName -NewName $cleanedBaseName | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment