# Script to organize texture files by checksum, moving all files to \common and duplicates to \common\duplicates # Usage: .\organize_textures.ps1 # Prompt user for texture folder path $textureFolderPath = Read-Host "Enter texture folder path (e.g., A:\1 Amazon_Active_Projects\1 BlenderAssets\Amazon\Char\Cartoon1\textures)" # Validate the input path if ([string]::IsNullOrWhiteSpace($textureFolderPath)) { Write-Host "Error: No path provided." -ForegroundColor Red exit } if (-not (Test-Path -Path $textureFolderPath -PathType Container)) { Write-Host "Error: Path does not exist or is not a directory: $textureFolderPath" -ForegroundColor Red exit } # Resolve the full path $textureFolderPath = (Resolve-Path $textureFolderPath).ProviderPath Write-Host "Processing texture folder: $textureFolderPath" -ForegroundColor Cyan # Get all files recursively, excluding the \common folder to avoid processing already-moved files Write-Host "Collecting files..." -ForegroundColor Yellow $allFiles = Get-ChildItem -Path $textureFolderPath -Recurse -File | Where-Object { $_.FullName -notlike "*\common\*" } if ($null -eq $allFiles -or $allFiles.Count -eq 0) { Write-Host "No files found to process (excluding \common folder)." -ForegroundColor Yellow exit } Write-Host "Found $($allFiles.Count) files to process." -ForegroundColor Green # Calculate checksums for all files Write-Host "Calculating checksums (this may take a while)..." -ForegroundColor Yellow $filesWithChecksums = @() $fileCount = 0 foreach ($file in $allFiles) { $fileCount++ if ($fileCount % 50 -eq 0) { Write-Host " Processed $fileCount / $($allFiles.Count) files..." -ForegroundColor Gray } try { $hash = Get-FileHash -Path $file.FullName -Algorithm SHA256 $filesWithChecksums += [PSCustomObject]@{ File = $file Hash = $hash.Hash } } catch { Write-Warning "Failed to calculate checksum for: $($file.FullName) - $($_.Exception.Message)" } } Write-Host "Checksum calculation complete." -ForegroundColor Green # Group files by checksum Write-Host "Grouping files by checksum..." -ForegroundColor Yellow $groupedByChecksum = $filesWithChecksums | Group-Object -Property Hash Write-Host "Found $($groupedByChecksum.Count) unique checksums." -ForegroundColor Green # Create \common and \common\duplicates directories $commonPath = Join-Path -Path $textureFolderPath -ChildPath "common" $duplicatesPath = Join-Path -Path $commonPath -ChildPath "duplicates" if (-not (Test-Path -Path $commonPath -PathType Container)) { New-Item -ItemType Directory -Path $commonPath | Out-Null Write-Host "Created directory: $commonPath" -ForegroundColor Green } if (-not (Test-Path -Path $duplicatesPath -PathType Container)) { New-Item -ItemType Directory -Path $duplicatesPath | Out-Null Write-Host "Created directory: $duplicatesPath" -ForegroundColor Green } # Track filenames already in \common to handle name conflicts $filesInCommon = @{} # Process each checksum group Write-Host "Moving files to \common and \common\duplicates..." -ForegroundColor Yellow $movedCount = 0 $duplicateCount = 0 foreach ($group in $groupedByChecksum) { $files = $group.Group if ($files.Count -eq 1) { # Single file - move to \common $fileObj = $files[0].File $fileName = $fileObj.Name $destinationPath = Join-Path -Path $commonPath -ChildPath $fileName # Handle name conflicts if ($filesInCommon.ContainsKey($fileName)) { # File with same name but different checksum already exists $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileName) $extension = [System.IO.Path]::GetExtension($fileName) $counter = 1 do { $newFileName = "${baseName}_${counter}${extension}" $destinationPath = Join-Path -Path $commonPath -ChildPath $newFileName $counter++ } while ($filesInCommon.ContainsKey($newFileName) -or (Test-Path -Path $destinationPath)) $fileName = $newFileName Write-Host " Name conflict resolved: renamed to '$fileName'" -ForegroundColor Yellow } try { Move-Item -Path $fileObj.FullName -Destination $destinationPath -Force $filesInCommon[$fileName] = $true $movedCount++ } catch { Write-Warning "Failed to move file: $($fileObj.FullName) - $($_.Exception.Message)" } } else { # Multiple files with same checksum (duplicates) # Move first file to \common $firstFile = $files[0].File $fileName = $firstFile.Name $destinationPath = Join-Path -Path $commonPath -ChildPath $fileName # Handle name conflicts for the first file if ($filesInCommon.ContainsKey($fileName)) { $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileName) $extension = [System.IO.Path]::GetExtension($fileName) $counter = 1 do { $newFileName = "${baseName}_${counter}${extension}" $destinationPath = Join-Path -Path $commonPath -ChildPath $newFileName $counter++ } while ($filesInCommon.ContainsKey($newFileName) -or (Test-Path -Path $destinationPath)) $fileName = $newFileName Write-Host " Name conflict resolved for duplicate group: renamed to '$fileName'" -ForegroundColor Yellow } try { Move-Item -Path $firstFile.FullName -Destination $destinationPath -Force $filesInCommon[$fileName] = $true $movedCount++ } catch { Write-Warning "Failed to move first duplicate file: $($firstFile.FullName) - $($_.Exception.Message)" } # Move remaining files to \common\duplicates with numbered suffixes for ($i = 1; $i -lt $files.Count; $i++) { $fileObj = $files[$i].File $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileObj.Name) $extension = [System.IO.Path]::GetExtension($fileObj.Name) $numberedFileName = "${baseName}.$($i.ToString('000'))${extension}" $duplicateDestinationPath = Join-Path -Path $duplicatesPath -ChildPath $numberedFileName # Handle name conflicts in duplicates folder $conflictCounter = 1 while (Test-Path -Path $duplicateDestinationPath) { $numberedFileName = "${baseName}.$($i.ToString('000'))_${conflictCounter}${extension}" $duplicateDestinationPath = Join-Path -Path $duplicatesPath -ChildPath $numberedFileName $conflictCounter++ } try { Move-Item -Path $fileObj.FullName -Destination $duplicateDestinationPath -Force $duplicateCount++ } catch { Write-Warning "Failed to move duplicate file: $($fileObj.FullName) - $($_.Exception.Message)" } } } } Write-Host "" Write-Host "File organization complete!" -ForegroundColor Green Write-Host " Files moved to \common: $movedCount" -ForegroundColor Cyan Write-Host " Duplicate files moved to \common\duplicates: $duplicateCount" -ForegroundColor Cyan