212 lines
7.2 KiB
C#
212 lines
7.2 KiB
C#
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/beatsaber";
|
|
|
|
private static string CacheRoot => Path.Combine(Application.persistentDataPath, "beatsaber");
|
|
private static string LegacyCacheRoot => 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}");
|
|
}
|
|
|
|
string legacyDir = LegacySongDir(songId);
|
|
if (Directory.Exists(legacyDir))
|
|
Directory.Delete(legacyDir, recursive: true);
|
|
}
|
|
|
|
public void DeleteDifficulty(SongInfo song, string difficulty)
|
|
{
|
|
TryMigrateLegacySong(song.id);
|
|
|
|
string path = MapPath(song, difficulty);
|
|
if (path != null && File.Exists(path))
|
|
File.Delete(path);
|
|
|
|
string songDir = SongDir(song.id);
|
|
if (Directory.Exists(songDir) && Directory.GetFileSystemEntries(songDir).Length == 0)
|
|
Directory.Delete(songDir);
|
|
}
|
|
|
|
public bool IsSongDownloaded(string songId)
|
|
{
|
|
TryMigrateLegacySong(songId);
|
|
return File.Exists(AudioPath(songId));
|
|
}
|
|
|
|
public bool IsDifficultyDownloaded(SongInfo song, string difficulty)
|
|
{
|
|
TryMigrateLegacySong(song.id);
|
|
|
|
string path = MapPath(song, difficulty);
|
|
return path != null && File.Exists(path);
|
|
}
|
|
|
|
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 || string.IsNullOrEmpty(info.mapFile)) return null;
|
|
string fileName = Path.GetFileName(info.mapFile);
|
|
if (string.IsNullOrEmpty(fileName)) return null;
|
|
return Path.Combine(SongDir(song.id), fileName);
|
|
}
|
|
|
|
// ── 내부 구현 ─────────────────────────────────────────────
|
|
|
|
private IEnumerator DownloadSongCoroutine(SongInfo song, string difficulty,
|
|
Action<float> onProgress, Action onComplete, Action<string> onError)
|
|
{
|
|
TryMigrateLegacySong(song.id);
|
|
|
|
string songDir = Path.GetFullPath(SongDir(song.id));
|
|
Directory.CreateDirectory(songDir);
|
|
|
|
// 1단계: 오디오 (70%)
|
|
string audioPath = Path.Combine(songDir, $"{song.id}.mp3");
|
|
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;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(diffInfo.mapFile))
|
|
{
|
|
onError?.Invoke($"'{difficulty}' 맵 파일 정보 없음 — Creator에서 곡을 다시 생성해주세요");
|
|
yield break;
|
|
}
|
|
|
|
string mapPath = MapPath(song, difficulty);
|
|
if (mapPath != null) mapPath = Path.GetFullPath(mapPath);
|
|
if (mapPath == null)
|
|
{
|
|
onError?.Invoke($"'{difficulty}' 맵 경로 계산 실패");
|
|
yield break;
|
|
}
|
|
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);
|
|
|
|
private static string LegacySongDir(string songId)
|
|
=> Path.Combine(LegacyCacheRoot, songId);
|
|
|
|
private static void TryMigrateLegacySong(string songId)
|
|
{
|
|
string sourceDir = LegacySongDir(songId);
|
|
string targetDir = SongDir(songId);
|
|
|
|
if (Directory.Exists(targetDir) || !Directory.Exists(sourceDir))
|
|
return;
|
|
|
|
CopyDirectory(sourceDir, targetDir);
|
|
Directory.Delete(sourceDir, recursive: true);
|
|
Debug.Log($"[DownloadManager] 기존 캐시를 영구 저장소로 이동: {songId}");
|
|
}
|
|
|
|
private static void CopyDirectory(string sourceDir, string targetDir)
|
|
{
|
|
Directory.CreateDirectory(targetDir);
|
|
|
|
foreach (string file in Directory.GetFiles(sourceDir))
|
|
{
|
|
string targetFile = Path.Combine(targetDir, Path.GetFileName(file));
|
|
File.Copy(file, targetFile, overwrite: true);
|
|
}
|
|
|
|
foreach (string dir in Directory.GetDirectories(sourceDir))
|
|
{
|
|
string targetSubDir = Path.Combine(targetDir, Path.GetFileName(dir));
|
|
CopyDirectory(dir, targetSubDir);
|
|
}
|
|
}
|
|
}
|