비트 찍기 완료 및 클로드를 통한 api작업

This commit is contained in:
jongjae0305
2026-05-20 16:44:28 +09:00
commit 2cd1be88d4
1596 changed files with 444234 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
using System;
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
public class DownloadManager : MonoBehaviour
{
[SerializeField] private string baseUrl = "http://whdwo798.synology.me:8180/beatsaber";
private static string CacheRoot => Path.Combine(Application.temporaryCachePath, "beatsaber");
// ── Public API ───────────────────────────────────────────
public void FetchSongsList(Action<SongsList> onSuccess, Action<string> onError = null)
{
StartCoroutine(GetText($"{baseUrl}/songs.json", json =>
{
SongsList list = JsonUtility.FromJson<SongsList>(json);
if (list == null)
onError?.Invoke("songs.json 파싱 실패");
else
onSuccess?.Invoke(list);
}, onError));
}
// 오디오 + 선택 난이도 맵 다운로드
public void DownloadSong(SongInfo song, string difficulty,
Action<float> onProgress, Action onComplete, Action<string> onError = null)
{
StartCoroutine(DownloadSongCoroutine(song, difficulty, onProgress, onComplete, onError));
}
public void DeleteSong(string songId)
{
string dir = SongDir(songId);
if (Directory.Exists(dir))
{
Directory.Delete(dir, recursive: true);
Debug.Log($"[DownloadManager] 삭제: {songId}");
}
}
public void DeleteDifficulty(SongInfo song, string difficulty)
{
string path = MapPath(song, difficulty);
if (path != null && File.Exists(path))
File.Delete(path);
}
public bool IsSongDownloaded(string songId)
=> File.Exists(AudioPath(songId));
public bool IsDifficultyDownloaded(SongInfo song, string difficulty)
{
string path = MapPath(song, difficulty);
return path != null && File.Exists(path);
}
// Spawner에서 재생 경로를 얻을 때 사용
public string AudioPath(string songId)
=> Path.Combine(SongDir(songId), $"{songId}.mp3");
public string MapPath(SongInfo song, string difficulty)
{
DifficultyInfo info = song.difficulties.Get(difficulty);
if (info == null) return null;
return Path.Combine(SongDir(song.id), Path.GetFileName(info.mapFile));
}
// ── 내부 구현 ─────────────────────────────────────────────
private IEnumerator DownloadSongCoroutine(SongInfo song, string difficulty,
Action<float> onProgress, Action onComplete, Action<string> onError)
{
Directory.CreateDirectory(SongDir(song.id));
// 1단계: 오디오 (70%)
string audioPath = AudioPath(song.id);
if (!File.Exists(audioPath))
{
bool failed = false;
yield return DownloadFile(
$"{baseUrl}/{song.audioFile}", audioPath,
p => onProgress?.Invoke(p * 0.7f),
err => { onError?.Invoke(err); failed = true; });
if (failed) yield break;
}
// 2단계: 맵 파일 (30%)
DifficultyInfo diffInfo = song.difficulties.Get(difficulty);
if (diffInfo == null)
{
onError?.Invoke($"난이도 '{difficulty}' 없음");
yield break;
}
string mapPath = MapPath(song, difficulty);
if (!File.Exists(mapPath))
{
bool failed = false;
yield return DownloadFile(
$"{baseUrl}/{diffInfo.mapFile}", mapPath,
p => onProgress?.Invoke(0.7f + p * 0.3f),
err => { onError?.Invoke(err); failed = true; });
if (failed) yield break;
}
onProgress?.Invoke(1f);
onComplete?.Invoke();
Debug.Log($"[DownloadManager] 완료: {song.title} ({difficulty})");
}
private IEnumerator DownloadFile(string url, string savePath,
Action<float> onProgress, Action<string> onError)
{
using var req = UnityWebRequest.Get(url);
req.downloadHandler = new DownloadHandlerFile(savePath);
req.SendWebRequest();
while (!req.isDone)
{
onProgress?.Invoke(req.downloadProgress);
yield return null;
}
if (req.result != UnityWebRequest.Result.Success)
{
if (File.Exists(savePath)) File.Delete(savePath);
onError?.Invoke($"다운로드 실패: {url} — {req.error}");
}
}
private IEnumerator GetText(string url, Action<string> onSuccess, Action<string> onError)
{
using var req = UnityWebRequest.Get(url);
yield return req.SendWebRequest();
if (req.result != UnityWebRequest.Result.Success)
onError?.Invoke($"요청 실패: {url} — {req.error}");
else
onSuccess?.Invoke(req.downloadHandler.text);
}
private static string SongDir(string songId)
=> Path.Combine(CacheRoot, songId);
}