154 lines
4.7 KiB
C#
154 lines
4.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
|
|
// 로컬에 다운로드된 곡 목록을 추적하고 persistentDataPath에 저장
|
|
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();
|
|
|
|
// ── Unity ────────────────────────────────────────────────
|
|
|
|
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();
|
|
}
|
|
|
|
// 플레이 시작 시 호출 → LRU 갱신
|
|
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;
|
|
}
|