Files
BeatSaber/Assets/Script/DownloadManager.cs
T

212 lines
7.2 KiB
C#
Raw Normal View History

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";
2026-05-28 19:01:20 +09:00
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}");
}
2026-05-28 19:01:20 +09:00
string legacyDir = LegacySongDir(songId);
if (Directory.Exists(legacyDir))
Directory.Delete(legacyDir, recursive: true);
}
public void DeleteDifficulty(SongInfo song, string difficulty)
{
2026-05-28 19:01:20 +09:00
TryMigrateLegacySong(song.id);
string path = MapPath(song, difficulty);
if (path != null && File.Exists(path))
File.Delete(path);
2026-05-28 19:01:20 +09:00
string songDir = SongDir(song.id);
if (Directory.Exists(songDir) && Directory.GetFileSystemEntries(songDir).Length == 0)
Directory.Delete(songDir);
}
public bool IsSongDownloaded(string songId)
2026-05-28 19:01:20 +09:00
{
TryMigrateLegacySong(songId);
return File.Exists(AudioPath(songId));
}
public bool IsDifficultyDownloaded(SongInfo song, string difficulty)
{
2026-05-28 19:01:20 +09:00
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)
{
2026-05-28 19:01:20 +09:00
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);
2026-05-28 19:01:20 +09:00
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);
}
}
}