141 lines
3.9 KiB
C#
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;
|
||
|
|
}
|