Skip to content

Commit 44d63b6

Browse files
committed
feat: 流派 / 版本分类查看和修改
1 parent 337bb28 commit 44d63b6

23 files changed

Lines changed: 1193 additions & 76 deletions
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using ChuChartManager.Models;
2+
using Microsoft.AspNetCore.Mvc;
3+
4+
namespace ChuChartManager.Controllers;
5+
6+
[ApiController]
7+
[Route("api/[controller]/[action]")]
8+
public class GenreController : ControllerBase
9+
{
10+
public record GenreItem(int Id, string Name, string AssetDir, int ColorR, int ColorG, int ColorB, bool IsCustom);
11+
public record AddGenreRequest(int Id, string AssetDir, string Name = "New Genre", int ColorR = 110, int ColorG = 217, int ColorB = 67);
12+
public record EditGenreRequest(string Name, int ColorR, int ColorG, int ColorB);
13+
14+
[HttpGet]
15+
public ActionResult<List<GenreItem>> GetAllGenres()
16+
{
17+
var gamePath = StaticSettings.GamePath;
18+
if (string.IsNullOrEmpty(gamePath)) return Ok(new List<GenreItem>());
19+
return Ok(BuildGenreItems(gamePath, StaticSettings.Scanner));
20+
}
21+
22+
[HttpPost]
23+
public ActionResult AddGenre([FromBody] AddGenreRequest req)
24+
{
25+
var gamePath = StaticSettings.GamePath;
26+
if (string.IsNullOrEmpty(gamePath)) return BadRequest("GamePath not set");
27+
if (string.IsNullOrWhiteSpace(req.AssetDir)) return BadRequest("Opt 不能为空");
28+
if (req.AssetDir == "A000") return BadRequest("不能在 A000 创建自定义流派");
29+
30+
var existing = BuildGenreItems(gamePath, StaticSettings.Scanner);
31+
var sort = GenreSortXml.LoadOrCreate(gamePath, req.AssetDir);
32+
if (existing.Any(g => g.Id == req.Id) && sort.Contains(req.Id)) return BadRequest($"ID {req.Id} 已存在");
33+
34+
sort.Add(req.Id, req.Name);
35+
sort.Save();
36+
return Ok();
37+
}
38+
39+
[HttpPost("{id:int}")]
40+
public ActionResult EditGenre(int id, [FromBody] EditGenreRequest req)
41+
{
42+
var gamePath = StaticSettings.GamePath;
43+
if (string.IsNullOrEmpty(gamePath)) return BadRequest("GamePath not set");
44+
45+
var edited = false;
46+
foreach (var sort in GenreSortXml.ScanAll(gamePath).Where(s => s.Contains(id)))
47+
{
48+
sort.SetName(id, req.Name);
49+
sort.Save();
50+
edited = true;
51+
}
52+
53+
foreach (var music in EnumerateMusics(StaticSettings.Scanner))
54+
{
55+
if (music.GenreId != id) continue;
56+
var root = music.XmlDoc.SelectSingleNode("/MusicData");
57+
var genreStrNode = root?.SelectSingleNode("genreNames/list/StringID/str");
58+
if (genreStrNode == null) continue;
59+
genreStrNode.InnerText = req.Name;
60+
music.Genres = [req.Name];
61+
music.Save();
62+
63+
var sort = GenreSortXml.LoadOrCreate(gamePath, music.AssetDir);
64+
sort.Add(id, req.Name);
65+
sort.Save();
66+
edited = true;
67+
}
68+
69+
return edited ? Ok() : NotFound();
70+
}
71+
72+
[HttpDelete("{id:int}")]
73+
public ActionResult DeleteGenre(int id)
74+
{
75+
var gamePath = StaticSettings.GamePath;
76+
if (string.IsNullOrEmpty(gamePath)) return BadRequest("GamePath not set");
77+
78+
var deleted = false;
79+
foreach (var sort in GenreSortXml.ScanAll(gamePath).Where(s => s.Contains(id)))
80+
{
81+
sort.Remove(id);
82+
sort.Save();
83+
deleted = true;
84+
}
85+
86+
return deleted ? Ok() : NotFound();
87+
}
88+
89+
private static List<GenreItem> BuildGenreItems(string gamePath, MusicScanner? scanner)
90+
{
91+
var map = new Dictionary<int, GenreItem>();
92+
foreach (var music in EnumerateMusics(scanner))
93+
{
94+
if (music.GenreId < 0 || map.ContainsKey(music.GenreId)) continue;
95+
var name = music.Genres.Count > 0 ? music.Genres[0] : "";
96+
if (!string.IsNullOrEmpty(name))
97+
map[music.GenreId] = new GenreItem(music.GenreId, name, music.AssetDir, 110, 217, 67, music.AssetDir != "A000");
98+
}
99+
100+
foreach (var sort in GenreSortXml.ScanAll(gamePath))
101+
{
102+
foreach (var (id, name) in sort.Entries)
103+
{
104+
if (map.ContainsKey(id)) continue;
105+
map[id] = new GenreItem(id, string.IsNullOrWhiteSpace(name) ? $"Genre {id}" : name, sort.AssetDir, 110, 217, 67, sort.AssetDir != "A000");
106+
}
107+
}
108+
109+
return map.Values.OrderBy(g => g.IsCustom).ThenBy(g => g.Id).ToList();
110+
}
111+
112+
private static IEnumerable<MusicXml> EnumerateMusics(MusicScanner? scanner)
113+
{
114+
if (scanner == null) yield break;
115+
foreach (var (_, musics) in scanner.MusicBySource)
116+
foreach (var music in musics)
117+
yield return music;
118+
}
119+
}

0 commit comments

Comments
 (0)