노래 만들기 수정 — NAS 업로드 완성, Easy 제거 및 ExpertPlus 추가, 다운로드 버그 수정
- NasPublisher: DSM 7.2 multipart body 수동 구성으로 업로드 401 오류 해결 - NasPublisher: 비밀번호 StreamingAssets/nas_config.json 분리, .gitignore 등록 - NasPublisher: staticBaseUrl 포트 8180 → 80 수정 - BeatSageUploader: Easy 난이도 제거, ExpertPlus(.dat) 추가 - NoteData: DifficultyMap에서 easy 제거, expertplus 추가 - SongCreatorManager: toggleEasy → toggleExpertPlus 교체 - SongDetailPanel: btnEasy → btnExpertPlus 교체 - DownloadManager: DownloadHandlerFile 경로 정규화(Path.GetFullPath), mapFile 빈 값 방어 처리 - PersistentXRRig: FindObjectsOfType obsolete 경고 수정 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,21 +20,22 @@ public class BeatSageUploader : MonoBehaviour
|
||||
private const float POLL_TIMEOUT = 300f;
|
||||
|
||||
// Beat Sage 난이도 이름 매핑 (내부 → API)
|
||||
// Beat Sage API가 인정하는 난이도: Normal, Hard, Expert, ExpertPlus (Easy 없음)
|
||||
private static readonly Dictionary<string, string> DiffNames = new()
|
||||
{
|
||||
{ "easy", "Easy" },
|
||||
{ "normal", "Normal" },
|
||||
{ "hard", "Hard" },
|
||||
{ "expert", "Expert" },
|
||||
{ "normal", "Normal" },
|
||||
{ "hard", "Hard" },
|
||||
{ "expert", "Expert" },
|
||||
{ "expertplus", "ExpertPlus" },
|
||||
};
|
||||
|
||||
// Beat Sage .zip 내 .dat 파일명 매핑 (내부 → zip 내 파일명)
|
||||
private static readonly Dictionary<string, string> DatFileNames = new()
|
||||
{
|
||||
{ "easy", "Easy.dat" },
|
||||
{ "normal", "Normal.dat" },
|
||||
{ "hard", "Hard.dat" },
|
||||
{ "expert", "Expert.dat" },
|
||||
{ "normal", "Normal.dat" },
|
||||
{ "hard", "Hard.dat" },
|
||||
{ "expert", "Expert.dat" },
|
||||
{ "expertplus", "ExpertPlus.dat" },
|
||||
};
|
||||
|
||||
public string CurrentStatus { get; private set; } = "";
|
||||
@@ -50,18 +51,20 @@ public class BeatSageUploader : MonoBehaviour
|
||||
Action<string> onError)
|
||||
{
|
||||
// 1단계: 레벨 생성 요청
|
||||
SetStatus("Beat Sage에 음원 전송 중...");
|
||||
SetStatus("[1/4] 음원 업로드 중...");
|
||||
Debug.Log($"[BeatSage] 업로드 시작 — 파일: {audioPath}");
|
||||
string levelId = null;
|
||||
|
||||
yield return CreateLevel(audioPath, difficulties,
|
||||
id => levelId = id,
|
||||
onError);
|
||||
|
||||
Debug.Log($"[BeatSage] CreateLevel 완료 — levelId: {levelId}");
|
||||
if (levelId == null) yield break;
|
||||
onProgress?.Invoke(0.15f);
|
||||
|
||||
// 2단계: 생성 완료 폴링
|
||||
SetStatus("Beat Sage AI 맵 생성 중...");
|
||||
SetStatus("[2/4] AI 맵 생성 시작...");
|
||||
bool ready = false;
|
||||
float elapsed = 0f;
|
||||
|
||||
@@ -74,8 +77,9 @@ public class BeatSageUploader : MonoBehaviour
|
||||
yield return PollHeartbeat(levelId,
|
||||
status =>
|
||||
{
|
||||
ready = status == "generated";
|
||||
error = status == "error";
|
||||
ready = string.Equals(status, "generated", System.StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(status, "done", System.StringComparison.OrdinalIgnoreCase);
|
||||
error = string.Equals(status, "error", System.StringComparison.OrdinalIgnoreCase);
|
||||
},
|
||||
onError);
|
||||
|
||||
@@ -83,13 +87,13 @@ public class BeatSageUploader : MonoBehaviour
|
||||
|
||||
float progress = Mathf.Clamp01(elapsed / POLL_TIMEOUT);
|
||||
onProgress?.Invoke(0.15f + progress * 0.6f);
|
||||
SetStatus($"Beat Sage AI 맵 생성 중... ({(int)elapsed}s)");
|
||||
SetStatus($"[2/4] AI 맵 생성 중... {(int)elapsed}초 경과");
|
||||
}
|
||||
|
||||
if (!ready) { onError?.Invoke("Beat Sage 타임아웃 (5분 초과)"); yield break; }
|
||||
|
||||
// 3단계: .zip 다운로드
|
||||
SetStatus("결과 다운로드 중...");
|
||||
SetStatus("[3/4] 결과 다운로드 중...");
|
||||
byte[] zipBytes = null;
|
||||
|
||||
yield return DownloadZip(levelId,
|
||||
@@ -100,7 +104,7 @@ public class BeatSageUploader : MonoBehaviour
|
||||
onProgress?.Invoke(0.9f);
|
||||
|
||||
// 4단계: .zip 해제 + BeatSageConverter 변환
|
||||
SetStatus("맵 데이터 변환 중...");
|
||||
SetStatus("[3/4] 맵 데이터 변환 중...");
|
||||
Dictionary<string, List<NoteData>> maps = null;
|
||||
|
||||
try
|
||||
@@ -114,7 +118,7 @@ public class BeatSageUploader : MonoBehaviour
|
||||
}
|
||||
|
||||
onProgress?.Invoke(1f);
|
||||
SetStatus("변환 완료");
|
||||
SetStatus("[3/4] 변환 완료");
|
||||
onComplete?.Invoke(maps);
|
||||
}
|
||||
|
||||
@@ -126,15 +130,25 @@ public class BeatSageUploader : MonoBehaviour
|
||||
byte[] audioBytes = File.ReadAllBytes(audioPath);
|
||||
string fileName = Path.GetFileName(audioPath);
|
||||
|
||||
// 난이도 문자열 변환: ["easy","hard"] → "Easy,Hard"
|
||||
var diffStr = string.Join(",", difficulties.ConvertAll(d =>
|
||||
DiffNames.TryGetValue(d, out var n) ? n : d));
|
||||
// 난이도: 알 수 없는 값(easy 등)은 건너뜀, 쉼표 구분 단일 필드로 전송
|
||||
var mappedDiffs = new List<string>();
|
||||
foreach (string d in difficulties)
|
||||
if (DiffNames.TryGetValue(d, out var n)) mappedDiffs.Add(n);
|
||||
|
||||
if (mappedDiffs.Count == 0)
|
||||
{
|
||||
onError?.Invoke("Beat Sage가 지원하지 않는 난이도입니다. Normal/Hard/Expert/ExpertPlus 중 선택하세요.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
string diffStr = string.Join(",", mappedDiffs);
|
||||
Debug.Log($"[BeatSage] 전송 difficulties: '{diffStr}'");
|
||||
|
||||
var form = new List<IMultipartFormSection>
|
||||
{
|
||||
new MultipartFormFileSection("audio_file", audioBytes, fileName, "audio/mpeg"),
|
||||
new MultipartFormDataSection("audio_metadata_title", ""),
|
||||
new MultipartFormDataSection("audio_metadata_artist", ""),
|
||||
new MultipartFormDataSection("audio_metadata_title", " "),
|
||||
new MultipartFormDataSection("audio_metadata_artist", " "),
|
||||
new MultipartFormDataSection("difficulties", diffStr),
|
||||
new MultipartFormDataSection("modes", "Standard"),
|
||||
new MultipartFormDataSection("events", "DotBlocks,Obstacles,Bombs"),
|
||||
@@ -149,6 +163,8 @@ public class BeatSageUploader : MonoBehaviour
|
||||
|
||||
if (req.result != UnityWebRequest.Result.Success)
|
||||
{
|
||||
string body = req.downloadHandler?.text ?? "(응답 없음)";
|
||||
Debug.LogError($"[BeatSage] HTTP {req.responseCode} — {req.error}\n응답 본문: {body}");
|
||||
onError?.Invoke($"레벨 생성 요청 실패: {req.error}");
|
||||
yield break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user