param(
[string]$LibraryRoot = "\\VaultOfData\AKAMedia\음악\처리됨\SUNO",
[switch]$Apply
)
$ErrorActionPreference = "Stop"
function ConvertTo-SafeName {
param([string]$Value)
$name = if ($Value) { $Value } else { "suno-track" }
$name = $name -replace '[\\/:*?"<>|]+', ' '
$name = $name -replace '\s+', ' '
$name = $name.Trim()
if (-not $name) { $name = "suno-track" }
if ($name.Length -gt 120) { $name = $name.Substring(0, 120).Trim() }
return $name
}
function Get-ShortId {
param([string]$Id)
if (-not $Id) { return "unknown-id" }
if ($Id.Length -le 12) { return $Id }
return $Id.Substring(0, 12)
}
function Get-NextTrackNumber {
param([string]$Folder)
if (-not (Test-Path -LiteralPath $Folder)) { return 1 }
$numbers = Get-ChildItem -LiteralPath $Folder -File -ErrorAction SilentlyContinue |
Where-Object { $_.Extension -in ".flac", ".mp3", ".m4a" } |
ForEach-Object {
if ($_.BaseName -match '^(\d{2})\s') { [int]$Matches[1] }
}
if (-not $numbers) { return 1 }
return (($numbers | Measure-Object -Maximum).Maximum + 1)
}
if (-not (Test-Path -LiteralPath $LibraryRoot)) {
throw "SUNO library root not found: $LibraryRoot"
}
$candidateDirs = Get-ChildItem -LiteralPath $LibraryRoot -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match '^(?
.+)\s\[[0-9a-fA-F-]{8,}\]$' }
foreach ($dir in $candidateDirs) {
$metadata = Get-ChildItem -LiteralPath $dir.FullName -File -Filter "*.json" -ErrorAction SilentlyContinue |
ForEach-Object {
try {
$json = Get-Content -LiteralPath $_.FullName -Raw | ConvertFrom-Json
if ($json.id) {
[pscustomobject]@{ File = $_; Json = $json }
}
} catch {}
} |
Select-Object -First 1
if (-not $metadata) {
Write-Host "SKIP no metadata id: $($dir.FullName)"
continue
}
$id = "$($metadata.Json.id)"
$title = ConvertTo-SafeName "$($metadata.Json.title)"
if (-not $title -and $dir.Name -match '^(?.+)\s\[[0-9a-fA-F-]{8,}\]$') {
$title = ConvertTo-SafeName $Matches.title
}
$shortId = Get-ShortId $id
$targetDir = Join-Path $LibraryRoot $title
$trackNo = Get-NextTrackNumber $targetDir
$audio = Get-ChildItem -LiteralPath $dir.FullName -File -ErrorAction SilentlyContinue |
Where-Object { $_.Extension -in ".flac", ".mp3", ".m4a" } |
Select-Object -First 1
$cover = Get-ChildItem -LiteralPath $dir.FullName -File -Filter "cover.*" -ErrorAction SilentlyContinue | Select-Object -First 1
Write-Host ""
Write-Host "FROM: $($dir.FullName)"
Write-Host "TO: $targetDir"
Write-Host "ID: $id"
if (-not $Apply) {
Write-Host "DRY: would move audio/metadata/cover"
continue
}
New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
if ($audio) {
$newAudio = Join-Path $targetDir ("{0:D2} {1}{2}" -f $trackNo, $title, $audio.Extension)
Move-Item -LiteralPath $audio.FullName -Destination $newAudio -Force
Write-Host "audio -> $newAudio"
}
$newMetadata = Join-Path $targetDir ("metadata.{0}.json" -f $shortId)
Move-Item -LiteralPath $metadata.File.FullName -Destination $newMetadata -Force
Write-Host "metadata -> $newMetadata"
if ($cover) {
$coverTarget = Join-Path $targetDir "cover.jpg"
if (-not (Test-Path -LiteralPath $coverTarget)) {
Move-Item -LiteralPath $cover.FullName -Destination $coverTarget -Force
Write-Host "cover -> $coverTarget"
}
}
$remaining = Get-ChildItem -LiteralPath $dir.FullName -Force -ErrorAction SilentlyContinue
if (-not $remaining) {
Remove-Item -LiteralPath $dir.FullName -Force
Write-Host "removed empty old folder"
} else {
Write-Host "old folder kept; remaining files exist"
}
}