SongBrowserModel.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. using SongBrowserPlugin.DataAccess;
  2. using SongBrowserPlugin.UI;
  3. using SongLoaderPlugin;
  4. using SongLoaderPlugin.OverrideClasses;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Diagnostics;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Text.RegularExpressions;
  11. using UnityEngine;
  12. namespace SongBrowserPlugin
  13. {
  14. class FolderBeatMapData : BeatmapData
  15. {
  16. public FolderBeatMapData(BeatmapLineData[] beatmapLinesData, BeatmapEventData[] beatmapEventData) :
  17. base(beatmapLinesData, beatmapEventData)
  18. {
  19. }
  20. }
  21. class FolderBeatMapDataSO : BeatmapDataSO
  22. {
  23. public FolderBeatMapDataSO()
  24. {
  25. BeatmapLineData lineData = new BeatmapLineData();
  26. lineData.beatmapObjectsData = new BeatmapObjectData[0];
  27. this._beatmapData = new FolderBeatMapData(
  28. new BeatmapLineData[1]
  29. {
  30. lineData
  31. },
  32. new BeatmapEventData[1]
  33. {
  34. new BeatmapEventData(0, BeatmapEventType.Event0, 0)
  35. });
  36. }
  37. }
  38. class FolderLevel : StandardLevelSO
  39. {
  40. public void Init(String relativePath, String name, Sprite coverImage)
  41. {
  42. _songName = name;
  43. _songSubName = "";
  44. _songAuthorName = "Folder";
  45. _levelID = $"Folder_{relativePath}";
  46. var beatmapData = new FolderBeatMapDataSO();
  47. var difficultyBeatmaps = new List<CustomLevel.CustomDifficultyBeatmap>();
  48. var newDiffBeatmap = new CustomLevel.CustomDifficultyBeatmap(this, LevelDifficulty.Easy, 0, 0, beatmapData);
  49. difficultyBeatmaps.Add(newDiffBeatmap);
  50. var sceneInfo = Resources.Load<SceneInfo>("SceneInfo/" + "DefaultEnvironment" + "SceneInfo");
  51. this.InitFull(_levelID, _songName, _songSubName, _songAuthorName, SongLoaderPlugin.SongLoader.TemporaryAudioClip, 1, 1, 1, 1, 1, 1, 1, coverImage, difficultyBeatmaps.ToArray(), sceneInfo);
  52. this.InitData();
  53. }
  54. }
  55. class DirectoryNode
  56. {
  57. public string Key { get; private set; }
  58. public Dictionary<String, DirectoryNode> Nodes;
  59. public List<StandardLevelSO> Levels;
  60. public DirectoryNode(String key)
  61. {
  62. Key = key;
  63. Nodes = new Dictionary<string, DirectoryNode>();
  64. Levels = new List<StandardLevelSO>();
  65. }
  66. }
  67. public class SongBrowserModel
  68. {
  69. private const String CUSTOM_SONGS_DIR = "CustomSongs";
  70. private DateTime EPOCH = new DateTime(1970, 1, 1);
  71. private Logger _log = new Logger("SongBrowserModel");
  72. // song_browser_settings.xml
  73. private SongBrowserSettings _settings;
  74. // song list management
  75. private List<StandardLevelSO> _sortedSongs;
  76. private List<StandardLevelSO> _originalSongs;
  77. private Dictionary<String, SongLoaderPlugin.OverrideClasses.CustomLevel> _levelIdToCustomLevel;
  78. private SongLoaderPlugin.OverrideClasses.CustomLevelCollectionSO _gameplayModeCollection;
  79. private Dictionary<String, double> _cachedLastWriteTimes;
  80. private Dictionary<string, int> _weights;
  81. private Dictionary<String, DirectoryNode> _directoryTree;
  82. private Stack<DirectoryNode> _directoryStack = new Stack<DirectoryNode>();
  83. private GameplayMode _currentGamePlayMode;
  84. /// <summary>
  85. /// Toggle whether inverting the results.
  86. /// </summary>
  87. public bool InvertingResults { get; private set; }
  88. /// <summary>
  89. /// Get the settings the model is using.
  90. /// </summary>
  91. public SongBrowserSettings Settings
  92. {
  93. get
  94. {
  95. return _settings;
  96. }
  97. }
  98. /// <summary>
  99. /// Get the sorted song list for the current working directory.
  100. /// </summary>
  101. public List<StandardLevelSO> SortedSongList
  102. {
  103. get
  104. {
  105. return _sortedSongs;
  106. }
  107. }
  108. /// <summary>
  109. /// Map LevelID to Custom Level info.
  110. /// </summary>
  111. public Dictionary<String, SongLoaderPlugin.OverrideClasses.CustomLevel> LevelIdToCustomSongInfos
  112. {
  113. get
  114. {
  115. return _levelIdToCustomLevel;
  116. }
  117. }
  118. /// <summary>
  119. /// How deep is the directory stack.
  120. /// </summary>
  121. public int DirStackSize
  122. {
  123. get
  124. {
  125. return _directoryStack.Count;
  126. }
  127. }
  128. /// <summary>
  129. /// Get the last selected (stored in settings) level id.
  130. /// </summary>
  131. public String LastSelectedLevelId
  132. {
  133. get
  134. {
  135. return _settings.currentLevelId;
  136. }
  137. set
  138. {
  139. _settings.currentLevelId = value;
  140. _settings.Save();
  141. }
  142. }
  143. public String CurrentDirectory
  144. {
  145. get
  146. {
  147. return _settings.currentDirectory;
  148. }
  149. set
  150. {
  151. _settings.currentDirectory = value;
  152. _settings.Save();
  153. }
  154. }
  155. /// <summary>
  156. /// Constructor.
  157. /// </summary>
  158. public SongBrowserModel()
  159. {
  160. _cachedLastWriteTimes = new Dictionary<String, double>();
  161. // Weights used for keeping the original songs in order
  162. // Invert the weights from the game so we can order by descending and make LINQ work with us...
  163. /* Level4, Level2, Level9, Level5, Level10, Level6, Level7, Level1, Level3, Level8, Level11 */
  164. _weights = new Dictionary<string, int>
  165. {
  166. ["Level4"] = 11,
  167. ["Level2"] = 10,
  168. ["Level9"] = 9,
  169. ["Level5"] = 8,
  170. ["Level10"] = 7,
  171. ["Level6"] = 6,
  172. ["Level7"] = 5,
  173. ["Level1"] = 4,
  174. ["Level3"] = 3,
  175. ["Level8"] = 2,
  176. ["Level11"] = 1
  177. };
  178. }
  179. /// <summary>
  180. /// Init this model.
  181. /// </summary>
  182. /// <param name="songSelectionMasterView"></param>
  183. /// <param name="songListViewController"></param>
  184. public void Init()
  185. {
  186. _settings = SongBrowserSettings.Load();
  187. _log.Info("Settings loaded, sorting mode is: {0}", _settings.sortMode);
  188. }
  189. /// <summary>
  190. /// Easy invert of toggling.
  191. /// </summary>
  192. public void ToggleInverting()
  193. {
  194. this.InvertingResults = !this.InvertingResults;
  195. }
  196. /// <summary>
  197. /// Get the song cache from the game.
  198. /// TODO: This might not even be necessary anymore. Need to test interactions with BeatSaverDownloader.
  199. /// </summary>
  200. public void UpdateSongLists(GameplayMode gameplayMode)
  201. {
  202. _currentGamePlayMode = gameplayMode;
  203. String customSongsPath = Path.Combine(Environment.CurrentDirectory, CUSTOM_SONGS_DIR);
  204. String cachedSongsPath = Path.Combine(customSongsPath, ".cache");
  205. DateTime currentLastWriteTIme = File.GetLastWriteTimeUtc(customSongsPath);
  206. IEnumerable<string> directories = Directory.EnumerateDirectories(customSongsPath, "*.*", SearchOption.AllDirectories);
  207. // Get LastWriteTimes
  208. foreach (var level in SongLoader.CustomLevels)
  209. {
  210. // Flip slashes, match SongLoaderPlugin
  211. //string slashed_dir = dir.Replace("\\", "/");
  212. //_log.Debug("Fetching LastWriteTime for {0}", slashed_dir);
  213. _cachedLastWriteTimes[level.levelID] = (File.GetLastWriteTimeUtc(level.customSongInfo.path) - EPOCH).TotalMilliseconds;
  214. }
  215. // Update song Infos, directory tree, and sort
  216. this.UpdateSongInfos(_currentGamePlayMode);
  217. this.UpdateDirectoryTree(customSongsPath);
  218. this.ProcessSongList();
  219. }
  220. /// <summary>
  221. /// Get the song infos from SongLoaderPluging
  222. /// </summary>
  223. private void UpdateSongInfos(GameplayMode gameplayMode)
  224. {
  225. _log.Trace("UpdateSongInfos for Gameplay Mode {0}", gameplayMode);
  226. // Get the level collection from song loader
  227. SongLoaderPlugin.OverrideClasses.CustomLevelCollectionsForGameplayModes collections = SongLoaderPlugin.SongLoader.Instance.GetPrivateField<SongLoaderPlugin.OverrideClasses.CustomLevelCollectionsForGameplayModes>("_customLevelCollectionsForGameplayModes");
  228. _gameplayModeCollection = collections.GetCollection(gameplayMode) as SongLoaderPlugin.OverrideClasses.CustomLevelCollectionSO;
  229. _originalSongs = collections.GetLevels(gameplayMode).ToList();
  230. _sortedSongs = _originalSongs;
  231. _levelIdToCustomLevel = new Dictionary<string, SongLoaderPlugin.OverrideClasses.CustomLevel>();
  232. foreach (var level in SongLoader.CustomLevels)
  233. {
  234. if (!_levelIdToCustomLevel.Keys.Contains(level.levelID))
  235. _levelIdToCustomLevel.Add(level.levelID, level);
  236. }
  237. _log.Debug("Song Browser knows about {0} songs from SongLoader...", _sortedSongs.Count);
  238. }
  239. /// <summary>
  240. /// Make the directory tree.
  241. /// </summary>
  242. /// <param name="customSongsPath"></param>
  243. private void UpdateDirectoryTree(String customSongsPath)
  244. {
  245. // Determine folder mapping
  246. Uri customSongDirUri = new Uri(customSongsPath);
  247. _directoryTree = new Dictionary<string, DirectoryNode>();
  248. _directoryTree[CUSTOM_SONGS_DIR] = new DirectoryNode(CUSTOM_SONGS_DIR);
  249. foreach (StandardLevelSO level in _originalSongs)
  250. {
  251. AddItemToDirectoryTree(customSongDirUri, level);
  252. }
  253. // Determine starting location
  254. if (_directoryStack.Count < 1)
  255. {
  256. DirectoryNode currentNode = _directoryTree[CUSTOM_SONGS_DIR];
  257. _directoryStack.Push(currentNode);
  258. // Try to navigate directory path
  259. if (!String.IsNullOrEmpty(this.CurrentDirectory))
  260. {
  261. String[] paths = this.CurrentDirectory.Split('/');
  262. for (int i = 1; i < paths.Length; i++)
  263. {
  264. if (currentNode.Nodes.ContainsKey(paths[i]))
  265. {
  266. currentNode = currentNode.Nodes[paths[i]];
  267. _directoryStack.Push(currentNode);
  268. }
  269. }
  270. }
  271. }
  272. PrintDirectory(_directoryTree[CUSTOM_SONGS_DIR], 1);
  273. }
  274. /// <summary>
  275. /// Add a song to directory tree. Determine its place in the tree by walking the split directory path.
  276. /// </summary>
  277. /// <param name="customSongDirUri"></param>
  278. /// <param name="level"></param>
  279. private void AddItemToDirectoryTree(Uri customSongDirUri, StandardLevelSO level)
  280. {
  281. //_log.Debug("Processing item into directory tree: {0}", level.levelID);
  282. DirectoryNode currentNode = _directoryTree[CUSTOM_SONGS_DIR];
  283. // Just add original songs to root and bail
  284. if (level.levelID.Length < 32)
  285. {
  286. currentNode.Levels.Add(level);
  287. return;
  288. }
  289. CustomSongInfo songInfo = _levelIdToCustomLevel[level.levelID].customSongInfo;
  290. Uri customSongUri = new Uri(songInfo.path);
  291. Uri pathDiff = customSongDirUri.MakeRelativeUri(customSongUri);
  292. string relPath = Uri.UnescapeDataString(pathDiff.OriginalString);
  293. string[] paths = relPath.Split('/');
  294. Sprite folderIcon = Base64Sprites.Base64ToSprite(Base64Sprites.Folder);
  295. // Prevent cache directory from building into the tree, will add all its leafs to root.
  296. bool forceIntoRoot = false;
  297. _log.Debug("Processing path: {0}", songInfo.path);
  298. if (paths.Length > 2)
  299. {
  300. forceIntoRoot = paths[1].Contains(".cache");
  301. Regex r = new Regex(@"^\d{1,}-\d{1,}");
  302. if (r.Match(paths[1]).Success)
  303. {
  304. forceIntoRoot = true;
  305. }
  306. }
  307. for (int i = 1; i < paths.Length; i++)
  308. {
  309. string path = paths[i];
  310. if (path == Path.GetFileName(songInfo.path))
  311. {
  312. _log.Debug("\tLevel Found Adding {0}->{1}", currentNode.Key, level.levelID);
  313. currentNode.Levels.Add(level);
  314. break;
  315. }
  316. else if (currentNode.Nodes.ContainsKey(path))
  317. {
  318. currentNode = currentNode.Nodes[path];
  319. }
  320. else if (!forceIntoRoot)
  321. {
  322. currentNode.Nodes[path] = new DirectoryNode(path);
  323. FolderLevel folderLevel = new FolderLevel();
  324. folderLevel.Init(relPath, path, folderIcon);
  325. _log.Debug("\tAdding folder level {0}->{1}", currentNode.Key, path);
  326. currentNode.Levels.Add(folderLevel);
  327. _cachedLastWriteTimes[folderLevel.levelID] = (File.GetLastWriteTimeUtc(relPath) - EPOCH).TotalMilliseconds;
  328. currentNode = currentNode.Nodes[path];
  329. }
  330. }
  331. }
  332. /// <summary>
  333. /// Push a dir onto the stack.
  334. /// </summary>
  335. public void PushDirectory(IStandardLevel level)
  336. {
  337. DirectoryNode currentNode = _directoryStack.Peek();
  338. _log.Debug("Pushing directory {0}", level.songName);
  339. if (!currentNode.Nodes.ContainsKey(level.songName))
  340. {
  341. _log.Debug("Trying to push a directory that doesn't exist at this level.");
  342. return;
  343. }
  344. _directoryStack.Push(currentNode.Nodes[level.songName]);
  345. this.CurrentDirectory = level.levelID;
  346. ProcessSongList();
  347. }
  348. /// <summary>
  349. /// Pop a dir off the stack.
  350. /// </summary>
  351. public void PopDirectory()
  352. {
  353. if (_directoryStack.Count > 1)
  354. {
  355. _directoryStack.Pop();
  356. String currentDir = "";
  357. foreach (DirectoryNode node in _directoryStack)
  358. {
  359. currentDir = node.Key + "/" + currentDir;
  360. }
  361. this.CurrentDirectory = "Folder_" + currentDir;
  362. ProcessSongList();
  363. }
  364. }
  365. /// <summary>
  366. /// Print the directory structure parsed.
  367. /// </summary>
  368. /// <param name="node"></param>
  369. /// <param name="depth"></param>
  370. private void PrintDirectory(DirectoryNode node, int depth)
  371. {
  372. Console.WriteLine("Dir: {0}".PadLeft(depth*4, ' '), node.Key);
  373. node.Levels.ForEach(x => Console.WriteLine("{0}".PadLeft((depth + 1)*4, ' '), x.levelID));
  374. foreach (KeyValuePair<string, DirectoryNode> childNode in node.Nodes)
  375. {
  376. PrintDirectory(childNode.Value, depth + 1);
  377. }
  378. }
  379. /// <summary>
  380. /// Sort the song list based on the settings.
  381. /// </summary>
  382. private void ProcessSongList()
  383. {
  384. _log.Trace("ProcessSongList()");
  385. // This has come in handy many times for debugging issues with Newest.
  386. /*foreach (StandardLevelSO level in _originalSongs)
  387. {
  388. if (_levelIdToCustomLevel.ContainsKey(level.levelID))
  389. {
  390. _log.Debug("HAS KEY {0}: {1}", _levelIdToCustomLevel[level.levelID].customSongInfo.path, level.levelID);
  391. }
  392. else
  393. {
  394. _log.Debug("Missing KEY: {0}", level.levelID);
  395. }
  396. }*/
  397. Stopwatch stopwatch = Stopwatch.StartNew();
  398. _log.Debug("Showing songs for directory: {0}", _directoryStack.Peek().Key);
  399. List<StandardLevelSO> songList = _directoryStack.Peek().Levels;
  400. switch (_settings.sortMode)
  401. {
  402. case SongSortMode.Favorites:
  403. SortFavorites(songList);
  404. break;
  405. case SongSortMode.Original:
  406. SortOriginal(songList);
  407. break;
  408. case SongSortMode.Newest:
  409. SortNewest(songList);
  410. break;
  411. case SongSortMode.Author:
  412. SortAuthor(songList);
  413. break;
  414. case SongSortMode.PlayCount:
  415. SortPlayCount(songList, _currentGamePlayMode);
  416. break;
  417. case SongSortMode.Difficulty:
  418. SortDifficulty(songList);
  419. break;
  420. case SongSortMode.Random:
  421. SortRandom(songList);
  422. break;
  423. case SongSortMode.Search:
  424. SortSearch(songList);
  425. break;
  426. case SongSortMode.Default:
  427. default:
  428. SortSongName(songList);
  429. break;
  430. }
  431. if (this.InvertingResults && _settings.sortMode != SongSortMode.Random)
  432. {
  433. _sortedSongs.Reverse();
  434. }
  435. stopwatch.Stop();
  436. _log.Info("Sorting songs took {0}ms", stopwatch.ElapsedMilliseconds);
  437. }
  438. private void SortFavorites(List<StandardLevelSO> levels)
  439. {
  440. _log.Info("Sorting song list as favorites");
  441. _sortedSongs = levels
  442. .AsQueryable()
  443. .OrderBy(x => _settings.favorites.Contains(x.levelID) == false)
  444. .ThenBy(x => x.songName)
  445. .ThenBy(x => x.songAuthorName)
  446. .ToList();
  447. }
  448. private void SortOriginal(List<StandardLevelSO> levels)
  449. {
  450. _log.Info("Sorting song list as original");
  451. _sortedSongs = levels
  452. .AsQueryable()
  453. .OrderByDescending(x => _weights.ContainsKey(x.levelID) ? _weights[x.levelID] : 0)
  454. .ThenBy(x => x.songName)
  455. .ToList();
  456. }
  457. private void SortNewest(List<StandardLevelSO> levels)
  458. {
  459. _log.Info("Sorting song list as newest.");
  460. _sortedSongs = levels
  461. .AsQueryable()
  462. .OrderBy(x => _weights.ContainsKey(x.levelID) ? _weights[x.levelID] : 0)
  463. .ThenByDescending(x => x.levelID.StartsWith("Level") ? _weights[x.levelID] : _cachedLastWriteTimes[x.levelID])
  464. .ToList();
  465. }
  466. private void SortAuthor(List<StandardLevelSO> levels)
  467. {
  468. _log.Info("Sorting song list by author");
  469. _sortedSongs = levels
  470. .AsQueryable()
  471. .OrderBy(x => x.songAuthorName)
  472. .ThenBy(x => x.songName)
  473. .ToList();
  474. }
  475. private void SortPlayCount(List<StandardLevelSO> levels, GameplayMode gameplayMode)
  476. {
  477. _log.Info("Sorting song list by playcount");
  478. // Build a map of levelId to sum of all playcounts and sort.
  479. PlayerDynamicData playerData = GameDataModel.instance.gameDynamicData.GetCurrentPlayerDynamicData();
  480. IEnumerable<LevelDifficulty> difficultyIterator = Enum.GetValues(typeof(LevelDifficulty)).Cast<LevelDifficulty>();
  481. Dictionary<string, int> levelIdToPlayCount = new Dictionary<string, int>();
  482. foreach (var level in levels)
  483. {
  484. if (!levelIdToPlayCount.ContainsKey(level.levelID))
  485. {
  486. // Skip folders
  487. if (level.levelID.StartsWith("Folder_"))
  488. {
  489. levelIdToPlayCount.Add(level.levelID, 0);
  490. }
  491. else
  492. {
  493. int playCountSum = 0;
  494. foreach (LevelDifficulty difficulty in difficultyIterator)
  495. {
  496. PlayerLevelStatsData stats = playerData.GetPlayerLevelStatsData(level.levelID, difficulty, gameplayMode);
  497. playCountSum += stats.playCount;
  498. }
  499. levelIdToPlayCount.Add(level.levelID, playCountSum);
  500. }
  501. }
  502. }
  503. _sortedSongs = levels
  504. .AsQueryable()
  505. .OrderByDescending(x => levelIdToPlayCount[x.levelID])
  506. .ThenBy(x => x.songName)
  507. .ToList();
  508. }
  509. private void SortDifficulty(List<StandardLevelSO> levels)
  510. {
  511. _log.Info("Sorting song list by random");
  512. System.Random rnd = new System.Random(Guid.NewGuid().GetHashCode());
  513. Dictionary<LevelDifficulty, int> difficultyWeights = new Dictionary<LevelDifficulty, int>
  514. {
  515. [LevelDifficulty.Easy] = int.MaxValue - 4,
  516. [LevelDifficulty.Normal] = int.MaxValue - 3,
  517. [LevelDifficulty.Hard] = int.MaxValue - 2,
  518. [LevelDifficulty.Expert] = int.MaxValue - 1,
  519. [LevelDifficulty.ExpertPlus] = int.MaxValue,
  520. };
  521. IEnumerable<LevelDifficulty> difficultyIterator = Enum.GetValues(typeof(LevelDifficulty)).Cast<LevelDifficulty>();
  522. Dictionary<string, int> levelIdToDifficultyValue = new Dictionary<string, int>();
  523. foreach (var level in levels)
  524. {
  525. if (!levelIdToDifficultyValue.ContainsKey(level.levelID))
  526. {
  527. // Skip folders
  528. if (level.levelID.StartsWith("Folder_"))
  529. {
  530. levelIdToDifficultyValue.Add(level.levelID, 0);
  531. }
  532. else
  533. {
  534. int difficultyValue = 0;
  535. foreach (LevelDifficulty difficulty in difficultyIterator)
  536. {
  537. IStandardLevelDifficultyBeatmap beatmap = level.GetDifficultyLevel(difficulty);
  538. if (beatmap != null)
  539. {
  540. difficultyValue += difficultyWeights[difficulty];
  541. break;
  542. }
  543. }
  544. levelIdToDifficultyValue.Add(level.levelID, difficultyValue);
  545. }
  546. }
  547. }
  548. _sortedSongs = levels
  549. .AsQueryable()
  550. .OrderBy(x => levelIdToDifficultyValue[x.levelID])
  551. .ThenBy(x => x.songName)
  552. .ToList();
  553. }
  554. private void SortRandom(List<StandardLevelSO> levels)
  555. {
  556. _log.Info("Sorting song list by random");
  557. System.Random rnd = new System.Random(Guid.NewGuid().GetHashCode());
  558. _sortedSongs = levels
  559. .AsQueryable()
  560. .OrderBy(x => rnd.Next())
  561. .ToList();
  562. }
  563. private void SortSearch(List<StandardLevelSO> levels)
  564. {
  565. // Make sure we can actually search.
  566. if (this._settings.searchTerms.Count <= 0)
  567. {
  568. _log.Error("Tried to search for a song with no valid search terms...");
  569. SortSongName(levels);
  570. return;
  571. }
  572. string searchTerm = this._settings.searchTerms[0];
  573. if (String.IsNullOrEmpty(searchTerm))
  574. {
  575. _log.Error("Empty search term entered.");
  576. SortSongName(levels);
  577. return;
  578. }
  579. _log.Info("Sorting song list by search term: {0}", searchTerm);
  580. //_originalSongs.ForEach(x => _log.Debug($"{x.songName} {x.songSubName} {x.songAuthorName}".ToLower().Contains(searchTerm.ToLower()).ToString()));
  581. _sortedSongs = levels
  582. .AsQueryable()
  583. .Where(x => $"{x.songName} {x.songSubName} {x.songAuthorName}".ToLower().Contains(searchTerm.ToLower()))
  584. .ToList();
  585. //_sortedSongs.ForEach(x => _log.Debug(x.levelID));
  586. }
  587. private void SortSongName(List<StandardLevelSO> levels)
  588. {
  589. _log.Info("Sorting song list as default (songName)");
  590. _sortedSongs = levels
  591. .AsQueryable()
  592. .OrderBy(x => x.songName)
  593. .ThenBy(x => x.songAuthorName)
  594. .ToList();
  595. }
  596. }
  597. }