public IEnumerator RequestSongByLevelIDCoroutine(string levelId, Action <Song> callback) { UnityWebRequest wwwId = UnityWebRequest.Get($"{PluginConfig.beatsaverURL}/api/songs/search/hash/" + levelId); wwwId.timeout = 10; yield return(wwwId.SendWebRequest()); if (wwwId.isNetworkError || wwwId.isHttpError) { Logger.Error(wwwId.error); } else { #if DEBUG Logger.Log("Received response from BeatSaver..."); #endif JSONNode node = JSON.Parse(wwwId.downloadHandler.text); if (node["songs"].Count == 0) { Logger.Error($"Song {levelId} doesn't exist on BeatSaver!"); callback?.Invoke(null); yield break; } Song _tempSong = Song.FromSearchNode(node["songs"][0]); callback?.Invoke(_tempSong); } }
private List <BeatmapLevelSO> FilterSearch(List <BeatmapLevelSO> levels) { // Make sure we can actually search. if (this._settings.searchTerms.Count <= 0) { Logger.Error("Tried to search for a song with no valid search terms..."); SortSongName(levels); return(levels); } string searchTerm = this._settings.searchTerms[0]; if (String.IsNullOrEmpty(searchTerm)) { Logger.Error("Empty search term entered."); SortSongName(levels); return(levels); } Logger.Info("Filtering song list by search term: {0}", searchTerm); var terms = searchTerm.Split(' '); foreach (var term in terms) { levels = levels.Intersect( levels .Where(x => $"{x.songName} {x.songSubName} {x.songAuthorName}".ToLower().Contains(term.ToLower())) .ToList( ) ).ToList(); } return(levels); }
/// <summary> /// Callback, invoked when the data property is accessed. /// </summary> protected override byte[] GetData() { if (!isDone) { Logger.Error("{0}Downloading is not completed : {1}", kLog, m_WebRequest.url); throw new InvalidOperationException("Downloading is not completed. " + m_WebRequest.url); } else if (m_Buffer == null) { // Etag cache hit! if (m_WebRequest.responseCode == 304) { Logger.Debug("<color=green>{0}Etag cache hit : {1}</color>", kLog, m_WebRequest.url); m_Buffer = LoadCache(m_WebRequest.url); } // Download is completed successfully. else if (m_WebRequest.responseCode == 200) { Logger.Debug("<color=green>{0}Download is completed successfully : {1}</color>", kLog, m_WebRequest.url); m_Buffer = m_Stream.GetBuffer(); SaveCache(m_WebRequest.url, m_WebRequest.GetResponseHeader("Etag"), m_Buffer); } } if (m_Stream != null) { m_Stream.Dispose(); m_Stream = null; } return(m_Buffer); }
public IEnumerator RequestSongByLevelIDCoroutine(string levelId, Action <Song> callback) { UnityWebRequest wwwId = UnityWebRequest.Get($"{PluginConfig.beatsaverURL}/api/maps/by-hash/" + levelId); wwwId.timeout = 10; yield return(wwwId.SendWebRequest()); if (wwwId.isNetworkError || wwwId.isHttpError) { Logger.Error(wwwId.error); } else { JObject jNode = JObject.Parse(wwwId.downloadHandler.text); if (jNode.Children().Count() == 0) { Logger.Error($"Song {levelId} doesn't exist on BeatSaver!"); callback?.Invoke(null); yield break; } Song _tempSong = Song.FromSearchNode((JObject)jNode); callback?.Invoke(_tempSong); } }
/// <summary> /// Get the song cache from the game. /// </summary> public void UpdateLevelRecords() { Stopwatch timer = new Stopwatch(); timer.Start(); // Calculate some information about the custom song dir String customSongsPath = Path.Combine(Environment.CurrentDirectory, CUSTOM_SONGS_DIR); String revSlashCustomSongPath = customSongsPath.Replace('\\', '/'); double currentCustomSongDirLastWriteTIme = (File.GetLastWriteTimeUtc(customSongsPath) - EPOCH).TotalMilliseconds; bool customSongDirChanged = false; if (_customSongDirLastWriteTime != currentCustomSongDirLastWriteTIme) { customSongDirChanged = true; _customSongDirLastWriteTime = currentCustomSongDirLastWriteTIme; } if (!Directory.Exists(customSongsPath)) { Logger.Error("CustomSong directory is missing..."); return; } // Map some data for custom songs Regex r = new Regex(@"(\d+-\d+)", RegexOptions.IgnoreCase); Stopwatch lastWriteTimer = new Stopwatch(); lastWriteTimer.Start(); foreach (KeyValuePair <string, CustomPreviewBeatmapLevel> level in SongCore.Loader.CustomLevels) { // If we already know this levelID, don't both updating it. // SongLoader should filter duplicates but in case of failure we don't want to crash if (!_cachedLastWriteTimes.ContainsKey(level.Value.levelID) || customSongDirChanged) { double lastWriteTime = GetSongUserDate(level.Value); _cachedLastWriteTimes[level.Value.levelID] = lastWriteTime; } } lastWriteTimer.Stop(); Logger.Info("Determining song download time and determining mappings took {0}ms", lastWriteTimer.ElapsedMilliseconds); // Update song Infos, directory tree, and sort this.UpdatePlayCounts(); // Signal complete if (SongCore.Loader.CustomLevels.Count > 0) { didFinishProcessingSongs?.Invoke(SongCore.Loader.CustomLevels); } timer.Stop(); Logger.Info("Updating songs infos took {0}ms", timer.ElapsedMilliseconds); }
public IEnumerator DownloadScrappedData(Action <List <ScrappedSong> > callback) { Logger.Info("Downloading scrapped data..."); UnityWebRequest www; bool timeout = false; float time = 0f; UnityWebRequestAsyncOperation asyncRequest; try { www = UnityWebRequest.Get(scrappedDataURL); asyncRequest = www.SendWebRequest(); } catch (Exception e) { Logger.Exception("Exception hitting scrappedDataURL", e); yield break; } while (!asyncRequest.isDone) { yield return(null); time += Time.deltaTime; if (time >= 5f && asyncRequest.progress == 0f) { www.Abort(); timeout = true; Logger.Error("Connection timed out!"); } } if (www.isNetworkError || www.isHttpError || timeout) { Logger.Error("Unable to download scrapped data! " + (www.isNetworkError ? $"Network error: {www.error}" : (www.isHttpError ? $"HTTP error: {www.error}" : "Unknown error"))); } else { Logger.Info("Received response from github.com..."); Songs = JsonConvert.DeserializeObject <List <ScrappedSong> >(www.downloadHandler.text).OrderByDescending(x => x.Diffs.Count > 0 ? x.Diffs.Max(y => y.Stars) : 0).ToList(); callback?.Invoke(Songs); Logger.Info("Scrapped data downloaded!"); } }
private List <BeatmapLevelSO> FilterPlaylist() { // bail if no playlist, usually means the settings stored one the user then moved. if (this.CurrentPlaylist == null) { Logger.Error("Trying to load a null playlist..."); this.Settings.filterMode = SongFilterMode.None; return(null); } // Get song keys PlaylistsCollection.MatchSongsForPlaylist(this.CurrentPlaylist, true); Logger.Debug("Filtering songs for playlist: {0}", this.CurrentPlaylist.playlistTitle); Dictionary <String, BeatmapLevelSO> levelDict = new Dictionary <string, BeatmapLevelSO>(); foreach (KeyValuePair <string, List <BeatmapLevelSO> > entry in _levelPackToSongs) { foreach (BeatmapLevelSO level in entry.Value) { if (!levelDict.ContainsKey(level.levelID)) { levelDict.Add(level.levelID, level); } } } List <BeatmapLevelSO> songList = new List <BeatmapLevelSO>(); foreach (PlaylistSong ps in this.CurrentPlaylist.songs) { if (ps.level != null) { songList.Add(levelDict[ps.level.levelID]); } else { Logger.Debug("Could not find song in playlist: {0}", ps.songName); } } Logger.Debug("Playlist filtered song count: {0}", songList.Count); return(songList); }
/// <summary> /// Filter for a search query. /// </summary> /// <param name="levels"></param> /// <returns></returns> private List <IPreviewBeatmapLevel> FilterSearch(List <IPreviewBeatmapLevel> levels) { // Make sure we can actually search. if (this._settings.searchTerms.Count <= 0) { Logger.Error("Tried to search for a song with no valid search terms..."); SortSongName(levels); return(levels); } string searchTerm = this._settings.searchTerms[0]; if (String.IsNullOrEmpty(searchTerm)) { Logger.Error("Empty search term entered."); SortSongName(levels); return(levels); } Logger.Info("Filtering song list by search term: {0}", searchTerm); var terms = searchTerm.Split(' '); foreach (var term in terms) { levels = levels.Intersect( levels .Where(x => { var hash = SongBrowserModel.GetSongHash(x.levelID); var songKey = ""; if (SongDataCore.Plugin.Songs.Data.Songs.ContainsKey(hash)) { songKey = SongDataCore.Plugin.Songs.Data.Songs[hash].key; } return($"{songKey} {x.songName} {x.songSubName} {x.songAuthorName} {x.levelAuthorName}".ToLower().Contains(term.ToLower())); }) .ToList( ) ).ToList(); } return(levels); }
public IEnumerator RequestSongByKeyCoroutine(string key, Action <Song> callback) { UnityWebRequest wwwId = UnityWebRequest.Get($"{PluginConfig.beatsaverURL}/api/maps/detail/" + key); wwwId.timeout = 10; yield return(wwwId.SendWebRequest()); if (wwwId.isNetworkError || wwwId.isHttpError) { Logger.Error(wwwId.error); } else { JObject node = JObject.Parse(wwwId.downloadHandler.text); Song _tempSong = new Song((JObject)node, false); callback?.Invoke(_tempSong); } }
/// <summary> /// Wait for score saber related files to download. /// </summary> /// <returns></returns> private IEnumerator WaitForDownload() { if (ScoreSaberDatabaseDownloader.ScoreSaberDataFile != null) { Logger.Info("Using cached copy of ScoreSaberData..."); } else { SongBrowserApplication.MainProgressBar.ShowMessage("Downloading BeatStar data...", 5.0f); Logger.Info("Attempting to download: {0}", ScoreSaberDatabaseDownloader.SCRAPED_SCORE_SABER_JSON_URL); using (UnityWebRequest www = UnityWebRequest.Get(ScoreSaberDatabaseDownloader.SCRAPED_SCORE_SABER_JSON_URL)) { // Use 4MB cache, large enough for this file to grow for awhile. www.SetCacheable(new CacheableDownloadHandlerScoreSaberData(www, _buffer)); yield return(www.SendWebRequest()); Logger.Debug("Returned from web request!..."); try { ScoreSaberDatabaseDownloader.ScoreSaberDataFile = (www.downloadHandler as CacheableDownloadHandlerScoreSaberData).ScoreSaberDataFile; Logger.Info("Success downloading ScoreSaber data!"); SongBrowserApplication.MainProgressBar.ShowMessage("Success downloading BeatStar data...", 5.0f); onScoreSaberDataDownloaded?.Invoke(); } catch (System.InvalidOperationException) { Logger.Error("Failed to download ScoreSaber data file..."); } catch (Exception e) { Logger.Exception("Exception trying to download ScoreSaber data file...", e); } } } }
public IEnumerator RequestSongByKeyCoroutine(string key, Action <Song> callback) { UnityWebRequest wwwId = UnityWebRequest.Get($"{PluginConfig.beatsaverURL}/api/songs/detail/" + key); wwwId.timeout = 10; yield return(wwwId.SendWebRequest()); if (wwwId.isNetworkError || wwwId.isHttpError) { Logger.Error(wwwId.error); } else { #if DEBUG Logger.Log("Received response from BeatSaver..."); #endif JSONNode node = JSON.Parse(wwwId.downloadHandler.text); Song _tempSong = new Song(node["song"]); callback?.Invoke(_tempSong); } }
/// <summary> /// Get the song cache from the game. /// </summary> public void UpdateLevelRecords() { Stopwatch timer = new Stopwatch(); timer.Start(); // Calculate some information about the custom song dir String customSongsPath = Path.Combine(Environment.CurrentDirectory, CUSTOM_SONGS_DIR); String revSlashCustomSongPath = customSongsPath.Replace('\\', '/'); double currentCustomSongDirLastWriteTIme = (File.GetLastWriteTimeUtc(customSongsPath) - EPOCH).TotalMilliseconds; bool customSongDirChanged = false; if (_customSongDirLastWriteTime != currentCustomSongDirLastWriteTIme) { customSongDirChanged = true; _customSongDirLastWriteTime = currentCustomSongDirLastWriteTIme; } if (!Directory.Exists(customSongsPath)) { Logger.Error("CustomSong directory is missing..."); return; } IEnumerable <string> directories = Directory.EnumerateDirectories(customSongsPath, "*.*", SearchOption.AllDirectories); // Get LastWriteTimes Stopwatch lastWriteTimer = new Stopwatch(); lastWriteTimer.Start(); foreach (var level in SongLoader.CustomLevels) { // If we already know this levelID, don't both updating it. // SongLoader should filter duplicates but in case of failure we don't want to crash if (!_cachedLastWriteTimes.ContainsKey(level.levelID) || customSongDirChanged) { // Always use the newest date. var lastWriteTime = File.GetLastWriteTimeUtc(level.customSongInfo.path); var lastCreateTime = File.GetCreationTimeUtc(level.customSongInfo.path); var lastTime = lastWriteTime > lastCreateTime ? lastWriteTime : lastCreateTime; _cachedLastWriteTimes[level.levelID] = (lastTime - EPOCH).TotalMilliseconds; } if (!_levelIdToCustomLevel.ContainsKey(level.levelID)) { _levelIdToCustomLevel.Add(level.levelID, level); } if (!_levelIdToSongVersion.ContainsKey(level.levelID)) { DirectoryInfo info = new DirectoryInfo(level.customSongInfo.path); string currentDirectoryName = info.Name; String version = level.customSongInfo.path.Replace(revSlashCustomSongPath, "").Replace(currentDirectoryName, "").Replace("/", ""); if (!String.IsNullOrEmpty(version)) { _levelIdToSongVersion.Add(level.levelID, version); if (!_keyToSong.ContainsKey(version)) { _keyToSong.Add(version, level); } } } } lastWriteTimer.Stop(); Logger.Info("Determining song download time and determining mappings took {0}ms", lastWriteTimer.ElapsedMilliseconds); // Update song Infos, directory tree, and sort this.UpdateScoreSaberDataMapping(); this.UpdatePlayCounts(); // Check if we need to upgrade settings file favorites try { this.Settings.ConvertFavoritesToPlaylist(_levelIdToCustomLevel, _levelIdToSongVersion); } catch (Exception e) { Logger.Exception("FAILED TO CONVERT FAVORITES TO PLAYLIST!", e); } // load the current editing playlist or make one if (CurrentEditingPlaylist == null && !String.IsNullOrEmpty(this.Settings.currentEditingPlaylistFile)) { Logger.Debug("Loading playlist for editing: {0}", this.Settings.currentEditingPlaylistFile); CurrentEditingPlaylist = Playlist.LoadPlaylist(this.Settings.currentEditingPlaylistFile); PlaylistsCollection.MatchSongsForPlaylist(CurrentEditingPlaylist); } if (CurrentEditingPlaylist == null) { Logger.Debug("Current editing playlist does not exit, create..."); CurrentEditingPlaylist = new Playlist { playlistTitle = "Song Browser Favorites", playlistAuthor = "SongBrowser", fileLoc = this.Settings.currentEditingPlaylistFile, image = Base64Sprites.PlaylistIconB64, songs = new List <PlaylistSong>(), }; } CurrentEditingPlaylistLevelIds = new HashSet <string>(); foreach (PlaylistSong ps in CurrentEditingPlaylist.songs) { // Sometimes we cannot match a song if (ps.level == null) { continue; } CurrentEditingPlaylistLevelIds.Add(ps.level.levelID); } // Actually sort and filter //this.ProcessSongList(); // Signal complete if (SongLoader.CustomLevels.Count > 0) { didFinishProcessingSongs?.Invoke(SongLoader.CustomLevels); } timer.Stop(); Logger.Info("Updating songs infos took {0}ms", timer.ElapsedMilliseconds); }
public IEnumerator GetInfoForSong(Playlist playlist, PlaylistSong song, Action <Song> songCallback) { string url = ""; bool _usingHash = false; if (!string.IsNullOrEmpty(song.key)) { url = $"{PluginConfig.beatsaverURL}/api/songs/detail/{song.key}"; if (!string.IsNullOrEmpty(playlist.customDetailUrl)) { url = playlist.customDetailUrl + song.key; } } else if (!string.IsNullOrEmpty(song.hash)) { url = $"{PluginConfig.beatsaverURL}/api/songs/search/hash/{song.hash}"; _usingHash = true; } else if (!string.IsNullOrEmpty(song.levelId)) { string hash = CustomHelpers.CheckHex(song.levelId.Substring(0, Math.Min(32, song.levelId.Length))); url = $"{PluginConfig.beatsaverURL}/api/songs/search/hash/{hash}"; _usingHash = true; } else { yield break; } UnityWebRequest www = UnityWebRequest.Get(url); www.timeout = 15; yield return(www.SendWebRequest()); if (www.isNetworkError || www.isHttpError) { Logger.Error($"Unable to connect to {PluginConfig.beatsaverURL}! " + (www.isNetworkError ? $"Network error: {www.error}" : (www.isHttpError ? $"HTTP error: {www.error}" : "Unknown error"))); } else { try { JSONNode node = JSON.Parse(www.downloadHandler.text); if (_usingHash) { if (node["songs"].Count == 0) { Logger.Error($"Song {song.songName} doesn't exist on BeatSaver!"); songCallback?.Invoke(null); yield break; } songCallback?.Invoke(Song.FromSearchNode(node["songs"][0])); } else { songCallback?.Invoke(new Song(node["song"])); } } catch (Exception e) { Logger.Exception("Unable to parse response! Exception: " + e); } } }
public IEnumerator DownloadSongCoroutine(Song songInfo) { songInfo.songQueueState = SongQueueState.Downloading; UnityWebRequest www; bool timeout = false; float time = 0f; UnityWebRequestAsyncOperation asyncRequest; try { www = UnityWebRequest.Get(songInfo.downloadUrl); asyncRequest = www.SendWebRequest(); } catch (Exception e) { Logger.Error(e); songInfo.songQueueState = SongQueueState.Error; songInfo.downloadingProgress = 1f; yield break; } while ((!asyncRequest.isDone || songInfo.downloadingProgress < 1f) && songInfo.songQueueState != SongQueueState.Error) { yield return(null); time += Time.deltaTime; if (time >= 5f && asyncRequest.progress <= float.Epsilon) { www.Abort(); timeout = true; Logger.Error("Connection timed out!"); } songInfo.downloadingProgress = asyncRequest.progress; } if (songInfo.songQueueState == SongQueueState.Error && (!asyncRequest.isDone || songInfo.downloadingProgress < 1f)) { www.Abort(); } if (www.isNetworkError || www.isHttpError || timeout || songInfo.songQueueState == SongQueueState.Error) { songInfo.songQueueState = SongQueueState.Error; Logger.Error("Unable to download song! " + (www.isNetworkError ? $"Network error: {www.error}" : (www.isHttpError ? $"HTTP error: {www.error}" : "Unknown error"))); } else { Logger.Log("Received response from BeatSaver.com..."); string docPath = ""; string customSongsPath = ""; byte[] data = www.downloadHandler.data; Stream zipStream = null; try { docPath = Application.dataPath; docPath = docPath.Substring(0, docPath.Length - 5); docPath = docPath.Substring(0, docPath.LastIndexOf("/")); customSongsPath = docPath + "/CustomSongs/" + songInfo.id + "/"; if (!Directory.Exists(customSongsPath)) { Directory.CreateDirectory(customSongsPath); } zipStream = new MemoryStream(data); Logger.Log("Downloaded zip!"); } catch (Exception e) { Logger.Exception(e); songInfo.songQueueState = SongQueueState.Error; yield break; } yield return(new WaitWhile(() => _extractingZip)); //because extracting several songs at once sometimes hangs the game Task extract = ExtractZipAsync(songInfo, zipStream, customSongsPath); yield return(new WaitWhile(() => !extract.IsCompleted)); songDownloaded?.Invoke(songInfo); } }
/// <summary> /// Get the song cache from the game. /// </summary> public void UpdateLevelRecords() { Stopwatch timer = new Stopwatch(); timer.Start(); // Calculate some information about the custom song dir String customSongsPath = Path.Combine(Environment.CurrentDirectory, CUSTOM_SONGS_DIR); String revSlashCustomSongPath = customSongsPath.Replace('\\', '/'); double currentCustomSongDirLastWriteTIme = (File.GetLastWriteTimeUtc(customSongsPath) - EPOCH).TotalMilliseconds; bool customSongDirChanged = false; if (_customSongDirLastWriteTime != currentCustomSongDirLastWriteTIme) { customSongDirChanged = true; _customSongDirLastWriteTime = currentCustomSongDirLastWriteTIme; } if (!Directory.Exists(customSongsPath)) { Logger.Error("CustomSong directory is missing..."); return; } // Map some data for custom songs Regex r = new Regex(@"(\d+-\d+)", RegexOptions.IgnoreCase); Stopwatch lastWriteTimer = new Stopwatch(); lastWriteTimer.Start(); foreach (KeyValuePair <string, CustomPreviewBeatmapLevel> level in SongCore.Loader.CustomLevels) { // If we already know this levelID, don't both updating it. // SongLoader should filter duplicates but in case of failure we don't want to crash if (!_cachedLastWriteTimes.ContainsKey(level.Value.levelID) || customSongDirChanged) { double lastWriteTime = GetSongUserDate(level.Value); _cachedLastWriteTimes[level.Value.levelID] = lastWriteTime; } } lastWriteTimer.Stop(); Logger.Info("Determining song download time and determining mappings took {0}ms", lastWriteTimer.ElapsedMilliseconds); // Update song Infos, directory tree, and sort this.UpdatePlayCounts(); // Check if we need to upgrade settings file favorites try { this.Settings.ConvertFavoritesToPlaylist(SongCore.Loader.CustomLevels); } catch (Exception e) { Logger.Exception("FAILED TO CONVERT FAVORITES TO PLAYLIST!", e); } // load the current editing playlist or make one if (CurrentEditingPlaylist == null && !String.IsNullOrEmpty(this.Settings.currentEditingPlaylistFile)) { Logger.Debug("Loading playlist for editing: {0}", this.Settings.currentEditingPlaylistFile); CurrentEditingPlaylist = Playlist.LoadPlaylist(this.Settings.currentEditingPlaylistFile); PlaylistsCollection.MatchSongsForPlaylist(CurrentEditingPlaylist, true); } if (CurrentEditingPlaylist == null) { Logger.Debug("Current editing playlist does not exit, create..."); CurrentEditingPlaylist = new Playlist { playlistTitle = "Song Browser Favorites", playlistAuthor = "SongBrowser", fileLoc = this.Settings.currentEditingPlaylistFile, image = Base64Sprites.SpriteToBase64(Base64Sprites.BeastSaberLogo), songs = new List <PlaylistSong>(), }; } CurrentEditingPlaylistLevelIds = new HashSet <string>(); foreach (PlaylistSong ps in CurrentEditingPlaylist.songs) { // Sometimes we cannot match a song string levelId = null; if (ps.level != null) { levelId = ps.level.levelID; } else if (!String.IsNullOrEmpty(ps.levelId)) { levelId = ps.levelId; } else { //Logger.Debug("MISSING SONG {0}", ps.songName); continue; } CurrentEditingPlaylistLevelIds.Add(levelId); } // Signal complete if (SongCore.Loader.CustomLevels.Count > 0) { didFinishProcessingSongs?.Invoke(SongCore.Loader.CustomLevels); } timer.Stop(); Logger.Info("Updating songs infos took {0}ms", timer.ElapsedMilliseconds); }
public void DeleteSong(Song song) { bool zippedSong = false; string path = ""; // Console.WriteLine("Deleting: " + song.path); SongCore.Loader.Instance.DeleteSong(song.path, false); /* * CustomLevel level = SongLoader.CustomLevels.FirstOrDefault(x => x.levelID.StartsWith(song.hash)); * * if (level != null) * SongLoader.Instance.RemoveSongWithLevelID(level.levelID); * * if (string.IsNullOrEmpty(song.path)) * { * if (level != null) * path = level.customSongInfo.path; * } * else * { * path = song.path; * } */ path = song.path.Replace('\\', '/'); if (string.IsNullOrEmpty(path)) { return; } if (!Directory.Exists(path)) { return; } if (path.Contains("/.cache/")) { zippedSong = true; } Task.Run(() => { if (zippedSong) { Logger.Info("Deleting \"" + path.Substring(path.LastIndexOf('/')) + "\"..."); if (PluginConfig.deleteToRecycleBin) { FileOperationAPIWrapper.MoveToRecycleBin(path); } else { Directory.Delete(path, true); } string songHash = Directory.GetParent(path).Name; try { if (Directory.GetFileSystemEntries(path.Substring(0, path.LastIndexOf('/'))).Length == 0) { Logger.Info("Deleting empty folder \"" + path.Substring(0, path.LastIndexOf('/')) + "\"..."); Directory.Delete(path.Substring(0, path.LastIndexOf('/')), false); } } catch { Logger.Warning("Can't find or delete empty folder!"); } string docPath = Application.dataPath; docPath = docPath.Substring(0, docPath.Length - 5); docPath = docPath.Substring(0, docPath.LastIndexOf("/")); string customSongsPath = docPath + "/CustomSongs/"; string hash = ""; foreach (string file in Directory.GetFiles(customSongsPath, "*.zip")) { if (CreateMD5FromFile(file, out hash)) { if (hash == songHash) { File.Delete(file); break; } } } } else { try { Logger.Info("Deleting \"" + path.Substring(0, path.LastIndexOf('/')) + "\"..."); if (PluginConfig.deleteToRecycleBin) { FileOperationAPIWrapper.MoveToRecycleBin(path); } else { Directory.Delete(path, true); } try { if (Directory.GetFileSystemEntries(path.Substring(0, path.LastIndexOf('/'))).Length == 0) { Logger.Info("Deleting empty folder \"" + path.Substring(0, path.LastIndexOf('/')) + "\"..."); Directory.Delete(path.Substring(0, path.LastIndexOf('/')), false); } } catch { Logger.Warning("Unable to delete empty folder!"); } } catch (Exception ex) { Logger.Error("Error Deleting Song:" + song.path); Logger.Error(ex.ToString()); } } Logger.Info($"{_alreadyDownloadedSongs.RemoveAll(x => x.Compare(song))} song removed"); }).ConfigureAwait(false); }