Files
BeatSaber/Assets/Script/SongLibrary.cs
T
whdwo798 58c88dafff feat: SongSelect panel — NAS song list with download/play in Menu.unity
- Add DownloadManager, SongLibrary, SongSelectManager, SongDetailPanel scripts
- Rebuild SongSelect panel inside Menu.unity using VRBeatsKit style:
  left scroll list (ALL/OWNED tabs) + right detail panel (diff buttons, Download/Delete/Play)
- SceneBuilder replaced: only ③ Menu — Rebuild SongSelect Panel remains
- All UI text in English

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 01:22:16 +09:00

141 lines
3.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
public class SongLibrary : MonoBehaviour
{
public static SongLibrary Instance { get; private set; }
private const string FileName = "song_library.json";
private static string SavePath => Path.Combine(Application.persistentDataPath, FileName);
private LibraryData _data = new LibraryData();
private void Awake()
{
if (Instance != null) { Destroy(gameObject); return; }
Instance = this;
DontDestroyOnLoad(gameObject);
Load();
}
// ── Public API ───────────────────────────────────────────
public void MarkDownloaded(string songId, string difficulty)
{
LibraryEntry entry = GetOrCreate(songId);
if (!entry.difficulties.Contains(difficulty))
entry.difficulties.Add(difficulty);
entry.lastAccessedAt = DateTime.UtcNow.ToString("o");
Save();
}
public void MarkDifficultyRemoved(string songId, string difficulty)
{
LibraryEntry entry = Find(songId);
if (entry == null) return;
entry.difficulties.Remove(difficulty);
if (entry.difficulties.Count == 0)
_data.entries.Remove(entry);
Save();
}
public void MarkSongRemoved(string songId)
{
_data.entries.RemoveAll(e => e.songId == songId);
Save();
}
public void TouchSong(string songId)
{
LibraryEntry entry = Find(songId);
if (entry == null) return;
entry.lastAccessedAt = DateTime.UtcNow.ToString("o");
Save();
}
public bool IsSongDownloaded(string songId)
=> Find(songId) != null;
public bool IsDifficultyDownloaded(string songId, string difficulty)
=> Find(songId)?.difficulties.Contains(difficulty) ?? false;
public List<LibraryEntry> GetAll()
=> _data.entries;
public void ValidateWithFileSystem(DownloadManager dm, List<SongInfo> songs)
{
bool dirty = false;
foreach (SongInfo song in songs)
{
LibraryEntry entry = Find(song.id);
if (entry == null) continue;
if (!dm.IsSongDownloaded(song.id))
{
_data.entries.Remove(entry);
dirty = true;
continue;
}
entry.difficulties.RemoveAll(d => !dm.IsDifficultyDownloaded(song, d));
if (entry.difficulties.Count == 0)
{
_data.entries.Remove(entry);
dirty = true;
}
}
if (dirty) Save();
}
// ── 내부 구현 ─────────────────────────────────────────────
private LibraryEntry Find(string songId)
=> _data.entries.Find(e => e.songId == songId);
private LibraryEntry GetOrCreate(string songId)
{
LibraryEntry entry = Find(songId);
if (entry != null) return entry;
entry = new LibraryEntry { songId = songId };
_data.entries.Add(entry);
return entry;
}
private void Load()
{
if (!File.Exists(SavePath)) return;
try
{
string json = File.ReadAllText(SavePath);
_data = JsonUtility.FromJson<LibraryData>(json) ?? new LibraryData();
}
catch (Exception e)
{
Debug.LogWarning($"[SongLibrary] 로드 실패, 초기화: {e.Message}");
_data = new LibraryData();
}
}
private void Save()
{
File.WriteAllText(SavePath, JsonUtility.ToJson(_data, true));
}
}
[Serializable]
public class LibraryData
{
public List<LibraryEntry> entries = new List<LibraryEntry>();
}
[Serializable]
public class LibraryEntry
{
public string songId;
public List<string> difficulties = new List<string>();
public string lastAccessedAt;
}