Align Suno metadata with music library layout
This commit is contained in:
@@ -69,13 +69,15 @@ WAV 다운로드는 SUNO 계정 권한, 변환 준비 상태, CDN 접근 정책
|
|||||||
정리 포맷:
|
정리 포맷:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
\\VaultOfData\AKAMedia\음악\처리됨\SUNO\곡 제목 [clipId앞12자리]\01 곡 제목.flac
|
\\VaultOfData\AKAMedia\음악\처리됨\SUNO\곡 제목\01 곡 제목.flac
|
||||||
\\VaultOfData\AKAMedia\음악\처리됨\SUNO\곡 제목 [clipId앞12자리]\cover.jpg
|
\\VaultOfData\AKAMedia\음악\처리됨\SUNO\곡 제목\01 곡 제목.lrc
|
||||||
\\VaultOfData\AKAMedia\음악\처리됨\SUNO\곡 제목 [clipId앞12자리]\metadata.json
|
\\VaultOfData\AKAMedia\음악\처리됨\SUNO\곡 제목\cover.jpg
|
||||||
|
\\VaultOfData\AKAMedia\음악\처리됨\SUNO\곡 제목\album.nfo
|
||||||
|
\\VaultOfData\AKAMedia\음악\처리됨\SUNO\곡 제목\metadata.clipId앞12자리.json
|
||||||
```
|
```
|
||||||
|
|
||||||
동기화 비교 키는 곡 제목이 아니라 SUNO clip id입니다. 같은 제목의 곡이 여러 개 있어도 `metadata.json`의 `id`와 폴더명의 id 조각으로 구분합니다.
|
동기화 비교 키는 곡 제목이 아니라 SUNO clip id입니다. 같은 제목의 곡이 여러 개 있어도 폴더명/앨범명은 깨끗하게 `곡 제목`으로 유지하고, `metadata.*.json`의 `id`와 FLAC 내부 comment 태그로 구분합니다.
|
||||||
|
|
||||||
로컬 처리기는 manifest에 저장된 WAV URL만 다운로드합니다. MP3/audio fallback은 사용하지 않습니다. 이후 ffmpeg로 FLAC 변환하면서 기존 라이브러리 방식에 맞춰 title, artist, album, album_artist, track, disc, genre/style, lyrics-XXX, Suno clip id, cover art를 메타데이터로 넣습니다.
|
로컬 처리기는 manifest에 저장된 WAV URL만 다운로드합니다. MP3/audio fallback은 사용하지 않습니다. 이후 ffmpeg로 FLAC 변환하면서 기존 라이브러리 방식에 맞춰 title, artist, album, album_artist, track, disc, genre/style, lyrics-XXX, Suno clip id, cover art를 메타데이터로 넣습니다. 앨범명은 폴더명과 같은 `곡 제목`입니다.
|
||||||
|
|
||||||
로그는 `companion-logs` 폴더에 저장됩니다.
|
로그는 `companion-logs` 폴더에 저장됩니다.
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
+136
-22
@@ -29,6 +29,16 @@ function Resolve-Ffmpeg {
|
|||||||
throw "ffmpeg.exe를 찾지 못했어. -FfmpegPath 로 경로를 지정해줘."
|
throw "ffmpeg.exe를 찾지 못했어. -FfmpegPath 로 경로를 지정해줘."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Resolve-Ffprobe {
|
||||||
|
param([string]$Ffmpeg)
|
||||||
|
|
||||||
|
$candidate = Join-Path (Split-Path -Parent $Ffmpeg) "ffprobe.exe"
|
||||||
|
if (Test-Path -LiteralPath $candidate) { return $candidate }
|
||||||
|
$cmd = Get-Command ffprobe -ErrorAction SilentlyContinue
|
||||||
|
if ($cmd) { return $cmd.Source }
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
function Resolve-Manifest {
|
function Resolve-Manifest {
|
||||||
param([string]$Path)
|
param([string]$Path)
|
||||||
|
|
||||||
@@ -82,8 +92,7 @@ function Get-TrackFolder {
|
|||||||
)
|
)
|
||||||
|
|
||||||
$safeTitle = ConvertTo-SafeName $Title $Id
|
$safeTitle = ConvertTo-SafeName $Title $Id
|
||||||
$shortId = Get-ShortId $Id
|
return Join-Path $Root $safeTitle
|
||||||
return Join-Path $Root ("{0} [{1}]" -f $safeTitle, $shortId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Get-AlbumTitle {
|
function Get-AlbumTitle {
|
||||||
@@ -92,7 +101,20 @@ function Get-AlbumTitle {
|
|||||||
[string]$Id
|
[string]$Id
|
||||||
)
|
)
|
||||||
|
|
||||||
return "{0} [{1}]" -f (ConvertTo-SafeName $Title $Id), (Get-ShortId $Id)
|
return ConvertTo-SafeName $Title $Id
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Resolve-LibraryRoot {
|
function Resolve-LibraryRoot {
|
||||||
@@ -116,7 +138,7 @@ function Test-ExistingTrack {
|
|||||||
|
|
||||||
if (-not $Id -or -not (Test-Path -LiteralPath $Root)) { return $false }
|
if (-not $Id -or -not (Test-Path -LiteralPath $Root)) { return $false }
|
||||||
|
|
||||||
$metadataHit = Get-ChildItem -LiteralPath $Root -Recurse -Filter "*metadata.json" -File -ErrorAction SilentlyContinue |
|
$metadataHit = Get-ChildItem -LiteralPath $Root -Recurse -Filter "*.json" -File -ErrorAction SilentlyContinue |
|
||||||
Where-Object {
|
Where-Object {
|
||||||
try {
|
try {
|
||||||
$json = Get-Content -LiteralPath $_.FullName -Raw | ConvertFrom-Json
|
$json = Get-Content -LiteralPath $_.FullName -Raw | ConvertFrom-Json
|
||||||
@@ -224,7 +246,87 @@ function Add-MetadataArg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Escape-XmlText {
|
||||||
|
param([string]$Value)
|
||||||
|
|
||||||
|
return [System.Security.SecurityElement]::Escape($Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-AudioDuration {
|
||||||
|
param(
|
||||||
|
[string]$File,
|
||||||
|
[string]$Ffprobe
|
||||||
|
)
|
||||||
|
|
||||||
|
if (-not $Ffprobe -or -not (Test-Path -LiteralPath $File)) {
|
||||||
|
return [pscustomobject]@{ Seconds = 0; Text = "" }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$raw = & $Ffprobe -v quiet -print_format json -show_format $File
|
||||||
|
$json = $raw | ConvertFrom-Json
|
||||||
|
$seconds = [double]$json.format.duration
|
||||||
|
$span = [TimeSpan]::FromSeconds([Math]::Round($seconds))
|
||||||
|
return [pscustomobject]@{
|
||||||
|
Seconds = $seconds
|
||||||
|
Text = "{0:D2}:{1:D2}" -f [int]$span.TotalMinutes, $span.Seconds
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return [pscustomobject]@{ Seconds = 0; Text = "" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Write-AlbumNfo {
|
||||||
|
param(
|
||||||
|
[string]$Folder,
|
||||||
|
[string]$AlbumTitle,
|
||||||
|
[string]$Artist,
|
||||||
|
[string]$Genre,
|
||||||
|
[string]$Ffprobe
|
||||||
|
)
|
||||||
|
|
||||||
|
$tracks = Get-ChildItem -LiteralPath $Folder -File -ErrorAction SilentlyContinue |
|
||||||
|
Where-Object { $_.Extension -in ".flac", ".mp3", ".m4a" } |
|
||||||
|
Sort-Object Name
|
||||||
|
|
||||||
|
$trackXml = New-Object System.Collections.Generic.List[string]
|
||||||
|
$totalSeconds = 0
|
||||||
|
foreach ($file in $tracks) {
|
||||||
|
$position = 1
|
||||||
|
$title = [IO.Path]::GetFileNameWithoutExtension($file.Name)
|
||||||
|
if ($title -match '^(\d{2})\s+(.+)$') {
|
||||||
|
$position = [int]$Matches[1]
|
||||||
|
$title = $Matches[2]
|
||||||
|
}
|
||||||
|
$duration = Get-AudioDuration $file.FullName $Ffprobe
|
||||||
|
$totalSeconds += $duration.Seconds
|
||||||
|
$trackXml.Add(" <track>")
|
||||||
|
$trackXml.Add(" <position>$position</position>")
|
||||||
|
$trackXml.Add(" <title>$(Escape-XmlText $title)</title>")
|
||||||
|
if ($duration.Text) { $trackXml.Add(" <duration>$($duration.Text)</duration>") }
|
||||||
|
$trackXml.Add(" </track>")
|
||||||
|
}
|
||||||
|
|
||||||
|
$runtime = if ($totalSeconds -gt 0) { [Math]::Ceiling($totalSeconds / 60) } else { 0 }
|
||||||
|
$lines = New-Object System.Collections.Generic.List[string]
|
||||||
|
$lines.Add('<?xml version="1.0" encoding="utf-8" standalone="yes"?>')
|
||||||
|
$lines.Add('<album>')
|
||||||
|
$lines.Add(' <review />')
|
||||||
|
$lines.Add(' <outline />')
|
||||||
|
$lines.Add(' <lockdata>false</lockdata>')
|
||||||
|
$lines.Add(" <dateadded>$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')</dateadded>")
|
||||||
|
$lines.Add(" <title>$(Escape-XmlText $AlbumTitle)</title>")
|
||||||
|
if ($runtime -gt 0) { $lines.Add(" <runtime>$runtime</runtime>") }
|
||||||
|
if ($Genre) { $lines.Add(" <genre>$(Escape-XmlText $Genre)</genre>") }
|
||||||
|
$lines.Add(" <artist>$(Escape-XmlText $Artist)</artist>")
|
||||||
|
$lines.Add(" <albumartist>$(Escape-XmlText $Artist)</albumartist>")
|
||||||
|
foreach ($line in $trackXml) { $lines.Add($line) }
|
||||||
|
$lines.Add('</album>')
|
||||||
|
$lines | Set-Content -LiteralPath (Join-Path $Folder "album.nfo") -Encoding UTF8
|
||||||
|
}
|
||||||
|
|
||||||
$ffmpeg = Resolve-Ffmpeg $FfmpegPath
|
$ffmpeg = Resolve-Ffmpeg $FfmpegPath
|
||||||
|
$ffprobe = Resolve-Ffprobe $ffmpeg
|
||||||
$manifestFile = Resolve-Manifest $ManifestPath
|
$manifestFile = Resolve-Manifest $ManifestPath
|
||||||
$manifest = Get-Content -LiteralPath $manifestFile -Raw | ConvertFrom-Json
|
$manifest = Get-Content -LiteralPath $manifestFile -Raw | ConvertFrom-Json
|
||||||
$tracks = @($manifest.tracks)
|
$tracks = @($manifest.tracks)
|
||||||
@@ -241,6 +343,7 @@ New-Item -ItemType Directory -Force -Path $workRoot | Out-Null
|
|||||||
Write-Host "Manifest: $manifestFile"
|
Write-Host "Manifest: $manifestFile"
|
||||||
Write-Host "Output: $libraryRoot"
|
Write-Host "Output: $libraryRoot"
|
||||||
Write-Host "ffmpeg: $ffmpeg"
|
Write-Host "ffmpeg: $ffmpeg"
|
||||||
|
Write-Host "ffprobe: $ffprobe"
|
||||||
Write-Host "Tracks: $($tracks.Count)"
|
Write-Host "Tracks: $($tracks.Count)"
|
||||||
|
|
||||||
$index = 0
|
$index = 0
|
||||||
@@ -260,7 +363,8 @@ foreach ($track in $tracks) {
|
|||||||
New-Item -ItemType Directory -Force -Path $trackFolder | Out-Null
|
New-Item -ItemType Directory -Force -Path $trackFolder | Out-Null
|
||||||
$safeTitle = ConvertTo-SafeName $title $id
|
$safeTitle = ConvertTo-SafeName $title $id
|
||||||
$shortId = Get-ShortId $id
|
$shortId = Get-ShortId $id
|
||||||
$prefix = "{0} [{1}]" -f $safeTitle, $shortId
|
$trackNo = Get-NextTrackNumber $trackFolder
|
||||||
|
$prefix = "{0:D2} {1}" -f $trackNo, $safeTitle
|
||||||
|
|
||||||
$lyrics = Get-DeepValue $track @("prompt", "lyrics", "lyric", "text")
|
$lyrics = Get-DeepValue $track @("prompt", "lyrics", "lyric", "text")
|
||||||
$style = Get-DeepValue $track @("gpt_description_prompt", "style", "tags", "description")
|
$style = Get-DeepValue $track @("gpt_description_prompt", "style", "tags", "description")
|
||||||
@@ -276,9 +380,11 @@ foreach ($track in $tracks) {
|
|||||||
|
|
||||||
$inputAudio = Join-Path $workRoot "$prefix.input.wav"
|
$inputAudio = Join-Path $workRoot "$prefix.input.wav"
|
||||||
$rawCoverFile = Join-Path $workRoot "$prefix.cover.$(Get-UrlExtension $imageUrl 'jpg')"
|
$rawCoverFile = Join-Path $workRoot "$prefix.cover.$(Get-UrlExtension $imageUrl 'jpg')"
|
||||||
|
$trackCoverFile = Join-Path $workRoot "$prefix.embed-cover.jpg"
|
||||||
$coverFile = Join-Path $trackFolder "cover.jpg"
|
$coverFile = Join-Path $trackFolder "cover.jpg"
|
||||||
$metadataFile = Join-Path $trackFolder "metadata.json"
|
$metadataFile = Join-Path $trackFolder ("metadata.{0}.json" -f $shortId)
|
||||||
$flacFile = Join-Path $trackFolder ("01 {0}.flac" -f $safeTitle)
|
$lyricsFile = Join-Path $trackFolder ("{0}.lrc" -f $prefix)
|
||||||
|
$flacFile = Join-Path $trackFolder ("{0}.flac" -f $prefix)
|
||||||
|
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "[$index/$($tracks.Count)] $title"
|
Write-Host "[$index/$($tracks.Count)] $title"
|
||||||
@@ -298,16 +404,22 @@ foreach ($track in $tracks) {
|
|||||||
if ($imageUrl) {
|
if ($imageUrl) {
|
||||||
$coverDownloaded = Invoke-Download $imageUrl $rawCoverFile
|
$coverDownloaded = Invoke-Download $imageUrl $rawCoverFile
|
||||||
if ($coverDownloaded) {
|
if ($coverDownloaded) {
|
||||||
& $ffmpeg -hide_banner -y -i $rawCoverFile -frames:v 1 $coverFile
|
& $ffmpeg -hide_banner -y -i $rawCoverFile -frames:v 1 $trackCoverFile
|
||||||
$coverOk = ($LASTEXITCODE -eq 0) -and (Test-Path -LiteralPath $coverFile)
|
$coverOk = ($LASTEXITCODE -eq 0) -and (Test-Path -LiteralPath $trackCoverFile)
|
||||||
if (-not $coverOk) {
|
if (-not $coverOk) {
|
||||||
Copy-Item -LiteralPath $rawCoverFile -Destination $coverFile -Force
|
Copy-Item -LiteralPath $rawCoverFile -Destination $trackCoverFile -Force
|
||||||
$coverOk = Test-Path -LiteralPath $coverFile
|
$coverOk = Test-Path -LiteralPath $trackCoverFile
|
||||||
|
}
|
||||||
|
if ($coverOk -and -not (Test-Path -LiteralPath $coverFile)) {
|
||||||
|
Copy-Item -LiteralPath $trackCoverFile -Destination $coverFile -Force
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$track | ConvertTo-Json -Depth 50 | Set-Content -LiteralPath $metadataFile -Encoding UTF8
|
$track | ConvertTo-Json -Depth 50 | Set-Content -LiteralPath $metadataFile -Encoding UTF8
|
||||||
|
if ($lyrics) {
|
||||||
|
$lyrics | Set-Content -LiteralPath $lyricsFile -Encoding UTF8
|
||||||
|
}
|
||||||
|
|
||||||
$argsList = [System.Collections.Generic.List[string]]::new()
|
$argsList = [System.Collections.Generic.List[string]]::new()
|
||||||
$argsList.Add("-hide_banner")
|
$argsList.Add("-hide_banner")
|
||||||
@@ -316,7 +428,7 @@ foreach ($track in $tracks) {
|
|||||||
$argsList.Add($inputAudio)
|
$argsList.Add($inputAudio)
|
||||||
if ($coverOk) {
|
if ($coverOk) {
|
||||||
$argsList.Add("-i")
|
$argsList.Add("-i")
|
||||||
$argsList.Add($coverFile)
|
$argsList.Add($trackCoverFile)
|
||||||
$argsList.Add("-map")
|
$argsList.Add("-map")
|
||||||
$argsList.Add("0:a")
|
$argsList.Add("0:a")
|
||||||
$argsList.Add("-map")
|
$argsList.Add("-map")
|
||||||
@@ -332,20 +444,21 @@ foreach ($track in $tracks) {
|
|||||||
$argsList.Add("flac")
|
$argsList.Add("flac")
|
||||||
$argsList.Add("-compression_level")
|
$argsList.Add("-compression_level")
|
||||||
$argsList.Add("8")
|
$argsList.Add("8")
|
||||||
Add-MetadataArg $argsList "title" $title
|
Add-MetadataArg $argsList "TITLE" $title
|
||||||
Add-MetadataArg $argsList "artist" $ArtistName
|
Add-MetadataArg $argsList "ARTIST" $ArtistName
|
||||||
Add-MetadataArg $argsList "album" $albumTitle
|
Add-MetadataArg $argsList "ALBUM" $albumTitle
|
||||||
Add-MetadataArg $argsList "album_artist" $ArtistName
|
Add-MetadataArg $argsList "album_artist" $ArtistName
|
||||||
Add-MetadataArg $argsList "ARTISTS" $ArtistName
|
Add-MetadataArg $argsList "ARTISTS" $ArtistName
|
||||||
Add-MetadataArg $argsList "track" "1/1"
|
Add-MetadataArg $argsList "track" "$trackNo"
|
||||||
Add-MetadataArg $argsList "disc" "1/1"
|
Add-MetadataArg $argsList "disc" "1/1"
|
||||||
Add-MetadataArg $argsList "genre" $style
|
Add-MetadataArg $argsList "GENRE" $style
|
||||||
Add-MetadataArg $argsList "lyrics-XXX" $lyrics
|
Add-MetadataArg $argsList "lyrics-XXX" $lyrics
|
||||||
Add-MetadataArg $argsList "description" $style
|
Add-MetadataArg $argsList "DESCRIPTION" $style
|
||||||
Add-MetadataArg $argsList "comment" "Suno clip id: $id"
|
Add-MetadataArg $argsList "COMMENT" "Suno clip id: $id"
|
||||||
Add-MetadataArg $argsList "date" $createdAt
|
Add-MetadataArg $argsList "DATE" $createdAt
|
||||||
Add-MetadataArg $argsList "encoder" "Codex SUNO helper"
|
Add-MetadataArg $argsList "ENCODER" "Codex SUNO helper"
|
||||||
Add-MetadataArg $argsList "suno_model" $model
|
Add-MetadataArg $argsList "SUNO_MODEL" $model
|
||||||
|
Add-MetadataArg $argsList "SUNO_CLIP_ID" $id
|
||||||
Add-MetadataArg $argsList "TMED" "Digital Media"
|
Add-MetadataArg $argsList "TMED" "Digital Media"
|
||||||
$argsList.Add($flacFile)
|
$argsList.Add($flacFile)
|
||||||
|
|
||||||
@@ -356,6 +469,7 @@ foreach ($track in $tracks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "완료: $flacFile"
|
Write-Host "완료: $flacFile"
|
||||||
|
Write-AlbumNfo $trackFolder $albumTitle $ArtistName $style $ffprobe
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-not $KeepWorkFiles -and (Test-Path -LiteralPath $workRoot)) {
|
if (-not $KeepWorkFiles -and (Test-Path -LiteralPath $workRoot)) {
|
||||||
|
|||||||
+1
-1
@@ -222,7 +222,7 @@ function Get-ExistingSunoIds {
|
|||||||
return ,$ids
|
return ,$ids
|
||||||
}
|
}
|
||||||
|
|
||||||
Get-ChildItem -LiteralPath $Root -Recurse -Filter "*metadata.json" -File -ErrorAction SilentlyContinue | ForEach-Object {
|
Get-ChildItem -LiteralPath $Root -Recurse -Filter "*.json" -File -ErrorAction SilentlyContinue | ForEach-Object {
|
||||||
try {
|
try {
|
||||||
$json = Get-Content -LiteralPath $_.FullName -Raw | ConvertFrom-Json
|
$json = Get-Content -LiteralPath $_.FullName -Raw | ConvertFrom-Json
|
||||||
$id = "$($json.id)".Trim()
|
$id = "$($json.id)".Trim()
|
||||||
|
|||||||
Reference in New Issue
Block a user