33
44#include < set>
55
6+ #include < algorithm>
7+ #include < nlohmann/json.hpp>
8+ #include < fstream>
69#include " util/binary.h"
710
11+ using json = nlohmann::json;
812// sorting
913void SongList::Clear () {
1014 listMenuEntries.clear ();
@@ -86,7 +90,6 @@ void SongList::sortList(SortType sortType) {
8690 std::sort (songs.begin (), songs.end (), sortSource);
8791 break ;
8892 case SortType::Length:
89- std::sort (songs.begin (), songs.end (), sortTitle);
9093 std::sort (songs.begin (), songs.end (), sortLen);
9194 break ;
9295 case SortType::Year:
@@ -99,7 +102,11 @@ void SongList::sortList(SortType sortType) {
99102}
100103
101104void SongList::sortList (SortType sortType, int &selectedSong) {
102- Song &curSong = songs[selectedSong];
105+ Song curSong;
106+ bool hasCurrentSong = selectedSong >= 0 && selectedSong < songs.size ();
107+ if (hasCurrentSong) {
108+ curSong = songs[selectedSong];
109+ }
103110 selectedSong = 0 ;
104111 switch (sortType) {
105112 case SortType::Title:
@@ -114,7 +121,6 @@ void SongList::sortList(SortType sortType, int &selectedSong) {
114121 std::sort (songs.begin (), songs.end (), sortSource);
115122 break ;
116123 case SortType::Length:
117- std::sort (songs.begin (), songs.end (), sortTitle);
118124 std::sort (songs.begin (), songs.end (), sortLen);
119125 break ;
120126 case SortType::Year:
@@ -123,10 +129,12 @@ void SongList::sortList(SortType sortType, int &selectedSong) {
123129 break ;
124130 default :;
125131 }
126- for (int i = 0 ; i < songs.size (); i++) {
127- if (songs[i].artist == curSong.artist && songs[i].title == curSong.title ) {
128- selectedSong = i;
129- break ;
132+ if (hasCurrentSong) {
133+ for (size_t i = 0 ; i < songs.size (); i++) {
134+ if (songs[i].artist == curSong.artist && songs[i].title == curSong.title ) {
135+ selectedSong = i;
136+ break ;
137+ }
130138 }
131139 }
132140 listMenuEntries = GenerateSongEntriesWithHeaders (songs, sortType);
@@ -180,82 +188,157 @@ void SongList::ScanSongs(const std::vector<std::filesystem::path> &songsFolder)
180188 }
181189
182190 directoryCount++;
183- if (std::filesystem::exists (entry.path () / " info.json" )) {
184- Song song;
185- song.LoadSong (entry.path () / " info.json" );
186- songs.push_back (std::move (song));
187- songCount++;
191+ Song song;
192+ std::filesystem::path infoPath = entry.path () / " info.json" ;
193+ if (std::filesystem::exists (infoPath)) {
194+ song.LoadSong (infoPath);
195+ try {
196+ json infoData;
197+ std::ifstream infoFile (infoPath);
198+ infoFile >> infoData;
199+ infoFile.close ();
200+
201+ if (infoData.contains (" source" ) && infoData[" source" ].is_string ()) {
202+ song.source = infoData[" source" ].get <std::string>();
203+ if (song.source .empty ()) {
204+ song.source = " Unknown Source" ;
205+ }
206+ } else {
207+ song.source = " Unknown Source" ;
208+ }
209+
210+ if (infoData.contains (" release_year" ) && infoData[" release_year" ].is_string ()) {
211+ song.releaseYear = infoData[" release_year" ].get <std::string>();
212+ if (song.releaseYear .empty ()) {
213+ song.releaseYear = " Unknown Year" ;
214+ }
215+ } else {
216+ song.releaseYear = " Unknown Year" ;
217+ }
218+
219+ if (infoData.contains (" preview_start_time" ) && infoData[" preview_start_time" ].is_number_integer ()) {
220+ song.previewStartTime = infoData[" preview_start_time" ].get <int >();
221+ } else {
222+ song.previewStartTime = 3000 ;
223+ }
224+
225+ } catch (const std::exception& e) {
226+ song.source = " Unknown Source" ;
227+ song.releaseYear = " Unknown Year" ;
228+ song.previewStartTime = 500 ;
229+ }
188230 } else if (std::filesystem::exists (entry.path () / " song.ini" )) {
189- Song song;
190-
191231 song.songInfoPath = (entry.path () / " song.ini" ).string ();
192232 song.songDir = entry.path ().string ();
193233 song.LoadSongIni (entry.path ());
194234 song.ini = true ;
195- songs.push_back (std::move (song));
196- songCount++;
235+ if (std::filesystem::exists (infoPath)) {
236+ try {
237+ json infoData;
238+ std::ifstream infoFile (infoPath);
239+ infoFile >> infoData;
240+ infoFile.close ();
241+
242+ if (infoData.contains (" source" ) && infoData[" source" ].is_string ()) {
243+ song.source = infoData[" source" ].get <std::string>();
244+ if (song.source .empty ()) {
245+ song.source = " Unknown Source" ;
246+ }
247+ } else {
248+ song.source = " Unknown Source" ;
249+ }
250+
251+ if (infoData.contains (" release_year" ) && infoData[" release_year" ].is_string ()) {
252+ song.releaseYear = infoData[" release_year" ].get <std::string>();
253+ if (song.releaseYear .empty ()) {
254+ song.releaseYear = " Unknown Year" ;
255+ }
256+ } else {
257+ song.releaseYear = " Unknown Year" ;
258+ }
259+
260+ if (infoData.contains (" preview_start_time" ) && infoData[" preview_start_time" ].is_number_integer ()) {
261+ song.previewStartTime = infoData[" preview_start_time" ].get <int >();
262+ } else {
263+ song.previewStartTime = 500 ;
264+ }
265+
266+ Encore::EncoreLog (LOG_INFO, TextFormat (" CACHE: Read metadata for INI song %s - %s from %s" , song.title .c_str (), song.artist .c_str (), infoPath.string ().c_str ()));
267+ } catch (const std::exception& e) {
268+ Encore::EncoreLog (LOG_ERROR, TextFormat (" CACHE: Failed to read metadata for INI song %s - %s from %s: %s" , song.title .c_str (), song.artist .c_str (), infoPath.string ().c_str (), e.what ()));
269+ song.source = " Unknown Source" ;
270+ song.releaseYear = " Unknown Year" ;
271+ song.previewStartTime = 500 ;
272+ }
273+ } else {
274+ song.source = " Unknown Source" ;
275+ song.releaseYear = " Unknown Year" ;
276+ song.previewStartTime = 500 ;
277+ Encore::EncoreLog (LOG_INFO, TextFormat (" CACHE: No info.json for INI song %s - %s, using default metadata" , song.title .c_str (), song.artist .c_str ()));
278+ }
279+ } else {
280+ continue ;
197281 }
282+
283+ songs.push_back (std::move (song));
284+ songCount++;
198285 }
199286 }
200287
201288 Encore::EncoreLog (LOG_INFO, " CACHE: Rewriting song cache" );
202289 WriteCache ();
203290}
204291
292+ std::string GetLengthHeader (int length) {
293+ if (length < 60 ) return " < 1:00" ;
294+ if (length < 120 ) return " 1:00-2:00" ;
295+ if (length < 180 ) return " 2:00-3:00" ;
296+ if (length < 240 ) return " 3:00-4:00" ;
297+ if (length < 300 ) return " 4:00-5:00" ;
298+ return " 5:00+" ;
299+ }
300+
205301std::vector<ListMenuEntry> SongList::GenerateSongEntriesWithHeaders (
206302 const std::vector<Song> &songs, SortType sortType
207303) {
208304 std::vector<ListMenuEntry> songEntries;
209305 std::string currentHeader = " " ;
210306 int pos = 0 ;
211- for (int i = 0 ; i < songs.size (); i++) {
307+ for (size_t i = 0 ; i < songs.size (); i++) {
212308 const Song &song = songs[i];
309+ std::string header;
213310 switch (sortType) {
214311 case SortType::Title: {
215312 std::string title = removeArticle (TextToLower (song.title .c_str ()));
216- if (toupper (title[0 ]) != currentHeader[0 ]) {
217- currentHeader = toupper (title[0 ]);
218- songEntries.emplace_back (true , 0 , currentHeader, false );
219- pos++;
220- }
313+ header = title.empty () ? " #" : std::string (1 , toupper (title[0 ]));
221314 break ;
222315 }
223316 case SortType::Artist: {
224317 std::string artist = removeArticle (song.artist );
225- if (artist != currentHeader) {
226- currentHeader = song.artist ;
227- songEntries.emplace_back (true , 0 , currentHeader, false );
228- pos++;
229- }
318+ header = artist.empty () ? " #" : artist;
230319 break ;
231320 }
232321 case SortType::Source: {
233322 std::string source = removeArticle (song.source );
234- if (source != currentHeader) {
235- currentHeader = song.source ;
236- songEntries.emplace_back (true , 0 , currentHeader, false );
237- pos++;
238- }
323+ header = source.empty () ? " Unknown" : source;
239324 break ;
240325 }
241326 case SortType::Length: {
242- if (std::to_string (song.length ) != currentHeader) {
243- currentHeader = std::to_string (song.length );
244- songEntries.emplace_back (true , 0 , currentHeader, false );
245- pos++;
246- }
327+ header = GetLengthHeader (song.length );
247328 break ;
248329 }
249330 case SortType::Year: {
250- std::string year = removeArticle (song.releaseYear );
251- if (year != currentHeader) {
252- currentHeader = song.releaseYear ;
253- songEntries.emplace_back (true , 0 , currentHeader, false );
254- pos++;
255- }
331+ header = song.releaseYear .empty () ? " Unknown Year" : song.releaseYear ;
256332 break ;
257333 }
258- default :;
334+ default :
335+ header = " #" ;
336+ break ;
337+ }
338+ if (header != currentHeader) {
339+ currentHeader = header;
340+ songEntries.emplace_back (true , 0 , currentHeader, false );
341+ pos++;
259342 }
260343 songEntries.emplace_back (false , i, " " , false );
261344 pos++;
@@ -404,4 +487,4 @@ void SongList::LoadCache(const std::vector<std::filesystem::path> &songsFolder)
404487
405488 // ScanSongs(songsFolder);
406489 sortList (SortType::Title);
407- }
490+ }
0 commit comments