Skip to content

Instantly share code, notes, and snippets.

@bradwilson
Last active September 10, 2018 06:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bradwilson/1785350205a4016579b9ee905ae2f4de to your computer and use it in GitHub Desktop.
Save bradwilson/1785350205a4016579b9ee905ae2f4de to your computer and use it in GitHub Desktop.
normalize-names.ps1
param(
[string][Parameter(Mandatory = $true)] $Location,
[switch] $SkipFolderRename
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$oldEncoding = [Console]::OutputEncoding
$caseInsensitive = [StringComparison]"OrdinalIgnoreCase"
try {
[Console]::OutputEncoding = [Text.Encoding]::GetEncoding("windows-1252")
# Ensure $Location is normalized, so we can write sub-paths
$Location = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine((Get-Location), $Location.TrimEnd("\").TrimEnd("/")))
$LocationLength = $Location.Length + 1
function Get-LegalName {
param(
[string][Parameter(Mandatory = $true)] $Name,
[int] $MaxLength = 255
)
$result = ($Name -replace '[/\?<>\\:\*|"\^]', "_").TrimEnd("_")
if ($result.Length -gt $MaxLength) {
$result = $result.Substring(0, $MaxLength)
}
return $result
}
function Select-ParsedValue {
param(
[array] $Values,
[string] $Prefix
)
foreach ($value in $Values) {
$index = $value.IndexOf($Prefix, $caseInsensitive)
if ($index -gt -1) {
return $value.Substring($index + $Prefix.Length)
}
}
}
function Set-FileNameByTrackAndTitle {
param(
[string] $FilePath,
[string] $Track,
[string] $Title
)
if ($Track -and $Title) {
$originalName = Split-Path $FilePath -Leaf
$extension = [IO.Path]::GetExtension($originalName)
$paddedTrack = $Track.PadLeft(3, '0')
$maxLength = 254 - $paddedTrack.Length - $extension.Length
$legalTitle = Get-LegalName -Name $Title -MaxLength $maxLength
$newName = "$paddedTrack $legalTitle$extension"
if ($newName -cne $originalName) {
Write-Host "[F] $originalPath => $newName" -ForegroundColor Cyan
Rename-Item -LiteralPath $originalPath -NewName $newName
}
}
else {
Write-Host "Metadata missing: $originalPath" -ForegroundColor Yellow
}
}
function Sync-Folder {
param(
[string][Parameter(Mandatory = $true)] $Folder
)
Write-Progress -Activity $Folder
$artist = $null
$album = $null
### Rename the files
# MP3
Get-ChildItem -LiteralPath $Folder *.mp3 | Foreach-Object {
$originalPath = $_.FullName
# id3.exe does not seem to like paths with [ or ] in them, but supports wildcards
$searchPath = $originalPath.Replace("[", "?").Replace("]", "?")
$id3Info = (& id3 "$searchPath")
if (-not $artist) {
$artist = Select-ParsedValue -Values $id3Info -Prefix "Artist: "
}
if (-not $album) {
$album = Select-ParsedValue -Values $id3Info -Prefix "Album: "
}
$track = Select-ParsedValue -Values $id3Info -Prefix "Track: "
$title = Select-ParsedValue -Values $id3Info -Prefix "Title: "
Set-FileNameByTrackAndTitle -FilePath $originalPath -Track $track -Title $title
}
# FLAC
Get-ChildItem -LiteralPath $Folder *.flac | Foreach-Object {
$originalPath = $_.FullName
$flacInfo = (& metaflac --list --block-type=VORBIS_COMMENT "$originalPath")
if (-not $artist) {
$artist = Select-ParsedValue -Values $flacInfo -Prefix ": Artist="
}
if (-not $album) {
$album = Select-ParsedValue -Values $flacInfo -Prefix ": Album="
}
$track = Select-ParsedValue -Values $flacInfo -Prefix ": TrackNumber="
$title = Select-ParsedValue -Values $flacInfo -Prefix ": Title="
Set-FileNameByTrackAndTitle -FilePath $originalPath -Track $track -Title $title
}
### Recurse
Get-ChildItem -LiteralPath $Folder -Directory | Foreach-Object {
Sync-Folder -Folder $_.FullName
}
### Rename the containing folder
if ((-not $SkipFolderRename) -and $artist -and $album) {
$originalFolder = Split-Path $Folder -Leaf
if (($artist -eq $album) -or ($artist -eq "Various Artists")) {
$newFolder = $album
}
else {
$newFolder = "$artist - $album"
}
$newFolder = Get-LegalName -Name $newFolder
if ($newFolder.EndsWith(".")) {
$newFolder = $newFolder.Substring(0, $newFolder.Length - 1) + "_"
}
if ($originalFolder -cne $newFolder) {
Write-Host "[D] $Folder => $newFolder" -ForegroundColor Blue
# Hack around the fact that you can't rename just to change case
if ($originalFolder -eq $newFolder) {
$tempName = (New-Guid).Guid
$tempFullPath = Join-Path (Split-Path $Folder) $tempName
Rename-Item -LiteralPath $Folder -NewName $tempName
Rename-Item -LiteralPath $tempFullPath -NewName $newFolder
}
else {
Rename-Item -LiteralPath $Folder -NewName $newFolder
}
}
}
}
Sync-Folder -Folder $Location
}
finally {
[Console]::OutputEncoding = $oldEncoding
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment