116 lines
3.9 KiB
PowerShell
116 lines
3.9 KiB
PowerShell
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 '^(?<title>.+)\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 '^(?<title>.+)\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"
|
|
}
|
|
}
|