/// <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);
        }
 /// <summary>
 /// Update mapping of scrapped song data.
 /// </summary>
 private void OnBeatSaverDataDownloaded()
 {
     Logger.Trace("OnBeatSaverDataDownloaded");
     try
     {
         if (_songBrowserModel.Settings.sortMode.NeedsBeatSaverData())
         {
             _songBrowserUI.ProcessSongList();
             _songBrowserUI.RefreshSongUI();
         }
         else
         {
             _songBrowserUI.RefreshSortButtonUI();
         }
     }
     catch (Exception e)
     {
         Logger.Exception("Exception during OnDownloaderScrappedDataDownloaded: ", e);
     }
 }
Пример #3
0
        public void Refresh()
        {
            int removed = queuedSongs.RemoveAll(x => x.songQueueState == SongQueueState.Downloaded || x.songQueueState == SongQueueState.Error);

            Logger.Log($"Removed {removed} songs from queue");

            _queuedSongsTableView.ReloadData();
            _queuedSongsTableView.ScrollToCellWithIdx(0, TableViewScroller.ScrollPositionType.Beginning, true);

            if (queuedSongs.Count(x => x.songQueueState == SongQueueState.Downloading || x.songQueueState == SongQueueState.Queued) == 0)
            {
                Logger.Log("All songs downloaded!");
                allSongsDownloaded?.Invoke();
            }

            if (queuedSongs.Count(x => x.songQueueState == SongQueueState.Downloading) < PluginConfig.maxSimultaneousDownloads && queuedSongs.Any(x => x.songQueueState == SongQueueState.Queued))
            {
                StartCoroutine(DownloadSong(queuedSongs.First(x => x.songQueueState == SongQueueState.Queued)));
            }
        }
        /// <summary>
        /// Get a copy of the unfiltered, unsorted list of songs from level packs.
        /// </summary>
        public void UpdateLevelPackOriginalLists()
        {
            BeatmapLevelPackSO[] levelPacks = Resources.FindObjectsOfTypeAll <BeatmapLevelPackSO>();
            foreach (BeatmapLevelPackSO levelPack in levelPacks)
            {
                Logger.Debug("Attempting to get song list from levelPack: {0}...", levelPack);
                var beatmapLevelPack = levelPack as BeatmapLevelPackSO;

                // TODO - need to rethink interface here, not all level packs can be cast this high, some sort functions need it.
                //      - this helps prevent DLC from breaking everything
                if (beatmapLevelPack == null)
                {
                    continue;
                }

                _levelPackToSongs[levelPack.packName] = (beatmapLevelPack.beatmapLevelCollection as BeatmapLevelCollectionSO).GetPrivateField <BeatmapLevelSO[]>("_beatmapLevels").ToList();
                Logger.Debug("Got {0} songs from level collections...", _levelPackToSongs[levelPack.packName].Count);
                //_levelPackToSongs[levelPack.packName].ForEach(x => Logger.Debug("{0} by {1} = {2}", x.name, x.levelAuthorName, x.levelID));
            }
        }
        /// <summary>
        /// Attempt to sort by songs containing easy first
        /// </summary>
        /// <param name="levels"></param>
        /// <returns></returns>
        private List <IPreviewBeatmapLevel> SortDifficulty(List <IPreviewBeatmapLevel> levels)
        {
            Logger.Info("Sorting song list by difficulty (DISABLED!!!)...");

            /*
             * IEnumerable<BeatmapDifficulty> difficultyIterator = Enum.GetValues(typeof(BeatmapDifficulty)).Cast<BeatmapDifficulty>();
             * Dictionary<string, int> levelIdToDifficultyValue = new Dictionary<string, int>();
             * foreach (IPreviewBeatmapLevel level in levels)
             * {
             *  // only need to process a level once
             *  if (levelIdToDifficultyValue.ContainsKey(level.levelID))
             *  {
             *      continue;
             *  }
             *
             *  // TODO - fix, not honoring beatmap characteristic.
             *  int difficultyValue = 0;
             *  if (level as BeatmapLevelSO != null)
             *  {
             *      var beatmapSet = (level as BeatmapLevelSO).difficultyBeatmapSets;
             *      difficultyValue = beatmapSet
             *          .SelectMany(x => x.difficultyBeatmaps)
             *          .Sum(x => _difficultyWeights[x.difficulty]);
             *  }
             *  else if (_levelIdToCustomLevel.ContainsKey(level.levelID))
             *  {
             *      var beatmapSet = (_levelIdToCustomLevel[level.levelID] as CustomPreviewBeatmapLevel).standardLevelInfoSaveData.difficultyBeatmapSets;
             *      difficultyValue = beatmapSet
             *          .SelectMany(x => x.difficultyBeatmaps)
             *          .Sum(x => _difficultyWeights[(BeatmapDifficulty)Enum.Parse(typeof(BeatmapDifficulty), x.difficulty)]);
             *  }
             *
             *  levelIdToDifficultyValue.Add(level.levelID, difficultyValue);
             * }
             *
             * return levels
             *  .OrderBy(x => levelIdToDifficultyValue[x.levelID])
             *  .ThenBy(x => x.songName)
             *  .ToList();*/
            return(levels);
        }
Пример #6
0
        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>
        /// Parse the current pp data file.
        /// Public so controllers can decide when to update it.
        /// </summary>
        public void UpdateScoreSaberDataMapping()
        {
            Logger.Trace("UpdateScoreSaberDataMapping()");

            ScoreSaberDataFile scoreSaberDataFile = ScoreSaberDatabaseDownloader.ScoreSaberDataFile;

            // bail
            if (scoreSaberDataFile == null)
            {
                Logger.Warning("Score saber data is not ready yet...");
                return;
            }

            foreach (var level in SongLoader.CustomLevels)
            {
                // Skip
                if (_levelIdToScoreSaberData.ContainsKey(level.levelID))
                {
                    continue;
                }

                ScoreSaberData scoreSaberData = null;

                // try to version match first
                if (_levelIdToSongVersion.ContainsKey(level.levelID))
                {
                    String version = _levelIdToSongVersion[level.levelID];
                    if (scoreSaberDataFile.SongVersionToScoreSaberData.ContainsKey(version))
                    {
                        scoreSaberData = scoreSaberDataFile.SongVersionToScoreSaberData[version];
                    }
                }

                if (scoreSaberData != null)
                {
                    //Logger.Debug("{0} = {1}pp", level.songName, pp);
                    _levelIdToScoreSaberData.Add(level.levelID, scoreSaberData);
                }
            }
        }
Пример #8
0
        public IEnumerator DownloadPlaylist(Playlist playlist)
        {
            PlaylistsCollection.MatchSongsForPlaylist(playlist, true);

            List <PlaylistSong> needToDownload = playlist.songs.Where(x => x.level == null).ToList();

            Logger.Log($"Need to download {needToDownload.Count} songs");

            _downloadingPlaylist = true;
            foreach (var item in needToDownload)
            {
                Song beatSaverSong = null;

                if (String.IsNullOrEmpty(playlist.customArchiveUrl))
                {
                    Logger.Log("Obtaining hash and url for " + item.key + ": " + item.songName);
                    yield return(GetInfoForSong(playlist, item, (Song song) => { beatSaverSong = song; }));
                }
                else
                {
                    string archiveUrl = playlist.customArchiveUrl.Replace("[KEY]", item.key);

                    beatSaverSong = new Song()
                    {
                        songName            = item.songName,
                        id                  = item.key,
                        downloadingProgress = 0f,
                        hash                = (item.levelId == null ? "" : item.levelId),
                        downloadUrl         = archiveUrl
                    };
                }

                if (beatSaverSong != null && !SongLoader.CustomLevels.Any(x => x.levelID.Substring(0, 32) == beatSaverSong.hash.ToUpper()))
                {
                    _downloadQueueViewController.EnqueueSong(beatSaverSong, true);
                }
            }
            _downloadingPlaylist = false;
        }
Пример #9
0
        /// <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);
                    }
                }
            }
        }
        /// <summary>
        /// Try to refresh the song list.  Broken for now.
        /// </summary>
        public void RefreshSongList(string currentSelectedLevelId, bool scrollToLevel = true)
        {
            Logger.Info("Refreshing the song list view.");
            try
            {
                var levels = GetCurrentLevelPackLevels();

                Logger.Debug("Checking if TableView is initialized...");
                TableView tableView     = ReflectionUtil.GetPrivateField <TableView>(LevelPackLevelsTableView, "_tableView");
                bool      tableViewInit = ReflectionUtil.GetPrivateField <bool>(tableView, "_isInitialized");

                Logger.Debug("Reloading SongList TableView");
                tableView.ReloadData();

                Logger.Debug("Attempting to scroll to level...");
                String selectedLevelID = currentSelectedLevelId;
                if (!String.IsNullOrEmpty(currentSelectedLevelId))
                {
                    selectedLevelID = currentSelectedLevelId;
                }
                else
                {
                    if (levels.Length > 0)
                    {
                        selectedLevelID = levels.FirstOrDefault().levelID;
                    }
                }

                if (scrollToLevel)
                {
                    SelectAndScrollToLevel(LevelPackLevelsTableView, selectedLevelID);
                }
            }
            catch (Exception e)
            {
                Logger.Exception("Exception refreshing song list:", e);
            }
        }
        /// <summary>
        /// Sorting by star rating.
        /// </summary>
        /// <param name="levels"></param>
        /// <returns></returns>
        private List <IPreviewBeatmapLevel> SortStars(List <IPreviewBeatmapLevel> levels)
        {
            Logger.Info("Sorting song list by star points...");

            if (!SongDataCore.Plugin.Songs.IsDataAvailable())
            {
                SortWasMissingData = true;
                return(levels);
            }

            return(levels
                   .OrderByDescending(x =>
            {
                var hash = SongBrowserModel.GetSongHash(x.levelID);
                var stars = 0.0;
                if (SongDataCore.Plugin.Songs.Data.Songs.ContainsKey(hash))
                {
                    var diffs = SongDataCore.Plugin.Songs.Data.Songs[hash].diffs;
                    stars = diffs.Max(y => y.star);
                }

                //Logger.Debug("Stars={0}", stars);
                if (stars != 0)
                {
                    return stars;
                }

                if (_settings.invertSortResults)
                {
                    return double.MaxValue;
                }
                else
                {
                    return double.MinValue;
                }
            })
                   .ToList());
        }
        /// <summary>
        /// Sorting by BeatSaver heat stat.
        /// </summary>
        /// <param name="levelIds"></param>
        /// <returns></returns>
        private List <IPreviewBeatmapLevel> SortBeatSaverHeat(List <IPreviewBeatmapLevel> levelIds)
        {
            Logger.Info("Sorting song list by BeatSaver Heat!");

            // Do not always have data when trying to sort by heat
            if (!SongDataCore.Plugin.BeatSaver.IsDataAvailable())
            {
                return(levelIds);
            }

            return(levelIds
                   .OrderByDescending(x => {
                var hash = CustomHelpers.GetSongHash(x.levelID);
                if (SongDataCore.Plugin.BeatSaver.Data.Songs.ContainsKey(hash))
                {
                    return SongDataCore.Plugin.BeatSaver.Data.Songs[hash].stats.heat;
                }
                else
                {
                    return int.MinValue;
                }
            })
                   .ToList());
        }
Пример #13
0
        /// <summary>
        /// Set current level pack, reset all packs just in case.
        /// </summary>
        /// <param name="pack"></param>
        public void SetCurrentLevelPack(IBeatmapLevelPack pack)
        {
            Logger.Debug("Setting current level pack [{0}]: {1}", pack.packID, pack.packName);

            this.ResetLevelPacks();

            this._currentLevelPack = pack;

            var beatmapLevelPack = pack as BeatmapLevelPackSO;

            if (beatmapLevelPack == null)
            {
                Logger.Debug("DLC Detected...  Disabling SongBrowser...");
                _isPreviewLevelPack = true;
            }
            else
            {
                Logger.Debug("Owned level pack...  Enabling SongBrowser...");
                _isPreviewLevelPack = false;
            }

            this.Settings.currentLevelPackId = pack.packID;
            this.Settings.Save();
        }
Пример #14
0
        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);
            }
        }
Пример #15
0
        public static Button CreatePageButton(String name, RectTransform parent, String buttonTemplate, Vector2 anchoredPosition, Vector2 sizeDelta, UnityAction onClick, Sprite icon)
        {
            Logger.Debug("CreatePageButton({0}, {1}, {2}, {3}, {4}", name, parent, buttonTemplate, anchoredPosition, sizeDelta);
            Button btn = CreateBaseButton(name, parent, buttonTemplate);

            (btn.transform as RectTransform).anchorMin        = new Vector2(0.5f, 0.5f);
            (btn.transform as RectTransform).anchorMax        = new Vector2(0.5f, 0.5f);
            (btn.transform as RectTransform).anchoredPosition = anchoredPosition;
            (btn.transform as RectTransform).sizeDelta        = sizeDelta;
            (btn.transform as RectTransform).pivot            = new Vector2(0.5f, 0.5f);

            ButtonIconImage btnIcon = btn.gameObject.AddComponent <ButtonIconImage>();

            btnIcon.image        = btn.gameObject.GetComponentsInChildren <Image>(true).Where(x => x.gameObject.name == "Icon").FirstOrDefault();
            btnIcon.image.sprite = icon;

            btn.onClick.RemoveAllListeners();
            if (onClick != null)
            {
                btn.onClick.AddListener(onClick);
            }

            return(btn);
        }
Пример #16
0
        /// <summary>
        /// Select a level collection.
        /// </summary>
        /// <param name="levelCollectionName"></param>
        public void SelectLevelCollection(String levelCollectionName)
        {
            Logger.Trace("SelectLevelCollection({0})", levelCollectionName);

            try
            {
                IAnnotatedBeatmapLevelCollection collection = GetLevelCollectionByName(levelCollectionName);
                if (collection == null)
                {
                    Logger.Debug("Could not locate requested level collection...");
                    return;
                }

                Logger.Info("Selecting level collection: {0}", collection.collectionName);

                LevelFilteringNavigationController.SelectAnnotatedBeatmapLevelCollection(collection as IBeatmapLevelPack);

                Logger.Debug("Done selecting level collection!");
            }
            catch (Exception e)
            {
                Logger.Exception(e);
            }
        }
        /// <summary>
        /// Sorting by PP.
        /// </summary>
        /// <param name="levels"></param>
        /// <returns></returns>
        private List <IPreviewBeatmapLevel> SortPerformancePoints(List <IPreviewBeatmapLevel> levels)
        {
            Logger.Info("Sorting song list by performance points...");

            if (!SongDataCore.Plugin.ScoreSaber.IsDataAvailable())
            {
                return(levels);
            }

            return(levels
                   .OrderByDescending(x =>
            {
                var hash = CustomHelpers.GetSongHash(x.levelID);
                if (SongDataCore.Plugin.ScoreSaber.Data.Songs.ContainsKey(hash))
                {
                    return SongDataCore.Plugin.ScoreSaber.Data.Songs[hash].diffs.Max(y => y.pp);
                }
                else
                {
                    return 0;
                }
            })
                   .ToList());
        }
 /// <summary>
 /// Handle Party Mode
 /// </summary>
 /// <param name="arg1"></param>
 /// <param name="arg2"></param>
 private void HandlePartyModeSelection()
 {
     Logger.Trace("HandlePartyModeSelection()");
     HandleModeSelection(MainMenuViewController.MenuButton.Party);
     _songBrowserUI.Show();
 }
Пример #19
0
        /// <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);
        }
Пример #20
0
 /// <summary>
 /// Init this model.
 /// </summary>
 /// <param name="songSelectionMasterView"></param>
 /// <param name="songListViewController"></param>
 public void Init()
 {
     _settings = SongBrowserSettings.Load();
     Logger.Info("Settings loaded, sorting mode is: {0}", _settings.sortMode);
 }
Пример #21
0
        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);
                }
            }
        }
 /// <summary>
 /// Handle Multiplayer Mode.
 /// Triggers when level select is clicked inside a host lobby.
 /// </summary>
 /// <param name="arg1"></param>
 /// <param name="arg2"></param>
 private void HandleMultiplayerModeSelection()
 {
     Logger.Trace("HandleCampaignModeSelection()");
     HandleModeSelection(MainMenuViewController.MenuButton.Multiplayer);
     _songBrowserUI.Hide();
 }
Пример #23
0
        protected override void DidActivate(bool firstActivation, ActivationType type)
        {
            if (firstActivation && type == ActivationType.AddedToHierarchy)
            {
                gameObject.SetActive(true);
                rectTransform.sizeDelta = new Vector2(60f, 0f);

                _levelDetails = GetComponentsInChildren <StandardLevelDetailView>(true).First(x => x.name == "LevelDetail");
                _levelDetails.gameObject.SetActive(true);

                RemoveCustomUIElements(rectTransform);

                Destroy(GetComponentsInChildren <LevelParamsPanel>().First(x => x.name == "LevelParamsPanel").gameObject);

                RectTransform yourStats = GetComponentsInChildren <RectTransform>(true).First(x => x.name == "Stats");
                yourStats.gameObject.SetActive(true);

                //RectTransform buttonsRect = GetComponentsInChildren<RectTransform>().First(x => x.name == "PlayButtons");
                //buttonsRect.anchoredPosition = new Vector2(0f, 6f);

                TextMeshProUGUI[] _textComponents = GetComponentsInChildren <TextMeshProUGUI>();

                try
                {
                    songNameText = _textComponents.First(x => x.name == "SongNameText");
                    _textComponents.First(x => x.name == "Title").text = "Playlist";
                    songNameText.enableWordWrapping = true;

                    _textComponents.First(x => x.name == "Title" && x.transform.parent.name == "MaxCombo").text = "Author";
                    authorText = _textComponents.First(x => x.name == "Value" && x.transform.parent.name == "MaxCombo");
                    authorText.rectTransform.sizeDelta = new Vector2(24f, 0f);

                    _textComponents.First(x => x.name == "Title" && x.transform.parent.name == "Highscore").text = "Total songs";
                    totalSongsText = _textComponents.First(x => x.name == "Value" && x.transform.parent.name == "Highscore");

                    _textComponents.First(x => x.name == "Title" && x.transform.parent.name == "MaxRank").text = "Downloaded";
                    downloadedSongsText = _textComponents.First(x => x.name == "Value" && x.transform.parent.name == "MaxRank");
                }
                catch (Exception e)
                {
                    Logger.Exception("Unable to convert detail view controller! Exception:  " + e);
                }

                _selectButton = _levelDetails.playButton;
                _selectButton.SetButtonText(_selectButtonText);
                _selectButton.ToggleWordWrapping(false);
                _selectButton.onClick.RemoveAllListeners();
                _selectButton.onClick.AddListener(() => { selectButtonPressed?.Invoke(_currentPlaylist); });

                if (addDownloadButton)
                {
                    _downloadButton = _levelDetails.practiceButton;
                    _downloadButton.SetButtonIcon(Sprites.DownloadIcon);
                    _downloadButton.onClick.RemoveAllListeners();
                    _downloadButton.onClick.AddListener(() => { downloadButtonPressed?.Invoke(_currentPlaylist); });
                }
                else
                {
                    Destroy(_levelDetails.practiceButton.gameObject);
                }

                coverImage = _levelDetails.GetPrivateField <RawImage>("_coverImage");
            }
        }
        /// <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);
        }
Пример #25
0
        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);
            }
        }
Пример #26
0
        /// <summary>
        /// Sort the song list based on the settings.
        /// </summary>
        public void ProcessSongList()
        {
            Logger.Trace("ProcessSongList()");

            // This has come in handy many times for debugging issues with Newest.

            /*foreach (BeatmapLevelSO level in _originalSongs)
             * {
             *  if (_levelIdToCustomLevel.ContainsKey(level.levelID))
             *  {
             *      Logger.Debug("HAS KEY {0}: {1}", _levelIdToCustomLevel[level.levelID].customSongInfo.path, level.levelID);
             *  }
             *  else
             *  {
             *      Logger.Debug("Missing KEY: {0}", level.levelID);
             *  }
             * }*/

            // TODO - remove as part of unifying song list interface
            if (_isPreviewLevelPack)
            {
                return;
            }

            if (_levelPackToSongs.Count == 0)
            {
                Logger.Debug("Cannot process songs yet, level packs have not been processed...");
                return;
            }

            if (this._currentLevelPack == null || !this._levelPackToSongs.ContainsKey(this._currentLevelPack.packName))
            {
                Logger.Debug("Cannot process songs yet, no level pack selected...");
                return;
            }

            // Playlist filter will load the original songs.
            List <BeatmapLevelSO> unsortedSongs = null;
            List <BeatmapLevelSO> filteredSongs = null;

            if (this._settings.filterMode == SongFilterMode.Playlist && this.CurrentPlaylist != null)
            {
                unsortedSongs = null;
            }
            else
            {
                Logger.Debug("Using songs from level pack: {0}", this._currentLevelPack.packName);
                unsortedSongs = new List <BeatmapLevelSO>(_levelPackToSongs[this._currentLevelPack.packName]);
            }

            // filter
            Logger.Debug("Starting filtering songs...");
            Stopwatch stopwatch = Stopwatch.StartNew();

            switch (_settings.filterMode)
            {
            case SongFilterMode.Favorites:
                filteredSongs = FilterFavorites();
                break;

            case SongFilterMode.Search:
                filteredSongs = FilterSearch(unsortedSongs);
                break;

            case SongFilterMode.Playlist:
                filteredSongs = FilterPlaylist();
                break;

            case SongFilterMode.None:
            default:
                Logger.Info("No song filter selected...");
                filteredSongs = unsortedSongs;
                break;
            }

            stopwatch.Stop();
            Logger.Info("Filtering songs took {0}ms", stopwatch.ElapsedMilliseconds);

            // sort
            Logger.Debug("Starting to sort songs...");
            stopwatch = Stopwatch.StartNew();

            switch (_settings.sortMode)
            {
            case SongSortMode.Original:
                SortOriginal(filteredSongs);
                break;

            case SongSortMode.Newest:
                SortNewest(filteredSongs);
                break;

            case SongSortMode.Author:
                SortAuthor(filteredSongs);
                break;

            case SongSortMode.PlayCount:
                SortPlayCount(filteredSongs);
                break;

            case SongSortMode.PP:
                SortPerformancePoints(filteredSongs);
                break;

            case SongSortMode.Difficulty:
                SortDifficulty(filteredSongs);
                break;

            case SongSortMode.Random:
                SortRandom(filteredSongs);
                break;

            case SongSortMode.Default:
            default:
                SortSongName(filteredSongs);
                break;
            }

            if (this.Settings.invertSortResults && _settings.sortMode != SongSortMode.Random)
            {
                _sortedSongs.Reverse();
            }

            stopwatch.Stop();
            Logger.Info("Sorting songs took {0}ms", stopwatch.ElapsedMilliseconds);

            this.OverwriteCurrentLevelPack();
            //_sortedSongs.ForEach(x => Logger.Debug(x.levelID));
        }
        /// <summary>
        /// Sort the song list based on the settings.
        /// </summary>
        public void ProcessSongList(LevelPackLevelsViewController levelsViewController)
        {
            Logger.Trace("ProcessSongList()");

            List <IPreviewBeatmapLevel> unsortedSongs = null;
            List <IPreviewBeatmapLevel> filteredSongs = null;
            List <IPreviewBeatmapLevel> sortedSongs   = null;

            // Abort
            if (levelsViewController.levelPack == null)
            {
                Logger.Debug("Cannot process songs yet, no level pack selected...");
                return;
            }

            // fetch unsorted songs.
            // playlists always use customsongs
            if (this._settings.filterMode == SongFilterMode.Playlist && this.CurrentPlaylist != null)
            {
                unsortedSongs = null;
            }
            else
            {
                Logger.Debug("Using songs from level pack: {0}", levelsViewController.levelPack.packID);
                unsortedSongs = levelsViewController.levelPack.beatmapLevelCollection.beatmapLevels.ToList();
            }

            // filter
            Logger.Debug($"Starting filtering songs by {_settings.filterMode}");
            Stopwatch stopwatch = Stopwatch.StartNew();

            switch (_settings.filterMode)
            {
            case SongFilterMode.Favorites:
                filteredSongs = FilterFavorites();
                break;

            case SongFilterMode.Search:
                filteredSongs = FilterSearch(unsortedSongs);
                break;

            case SongFilterMode.Playlist:
                filteredSongs = FilterPlaylist();
                break;

            case SongFilterMode.Ranked:
                filteredSongs = FilterRanked(unsortedSongs, true, false);
                break;

            case SongFilterMode.Unranked:
                filteredSongs = FilterRanked(unsortedSongs, false, true);
                break;

            case SongFilterMode.Custom:
                Logger.Info("Song filter mode set to custom. Deferring filter behaviour to another mod.");
                filteredSongs = CustomFilterHandler != null?CustomFilterHandler.Invoke(levelsViewController.levelPack) : unsortedSongs;

                break;

            case SongFilterMode.None:
            default:
                Logger.Info("No song filter selected...");
                filteredSongs = unsortedSongs;
                break;
            }

            stopwatch.Stop();
            Logger.Info("Filtering songs took {0}ms", stopwatch.ElapsedMilliseconds);

            // sort
            Logger.Debug("Starting to sort songs...");
            stopwatch = Stopwatch.StartNew();

            switch (_settings.sortMode)
            {
            case SongSortMode.Original:
                sortedSongs = SortOriginal(filteredSongs);
                break;

            case SongSortMode.Newest:
                sortedSongs = SortNewest(filteredSongs);
                break;

            case SongSortMode.Author:
                sortedSongs = SortAuthor(filteredSongs);
                break;

            case SongSortMode.UpVotes:
                sortedSongs = SortUpVotes(filteredSongs);
                break;

            case SongSortMode.PlayCount:
                sortedSongs = SortBeatSaverPlayCount(filteredSongs);
                break;

            case SongSortMode.Rating:
                sortedSongs = SortBeatSaverRating(filteredSongs);
                break;

            case SongSortMode.Heat:
                sortedSongs = SortBeatSaverHeat(filteredSongs);
                break;

            case SongSortMode.YourPlayCount:
                sortedSongs = SortPlayCount(filteredSongs);
                break;

            case SongSortMode.PP:
                sortedSongs = SortPerformancePoints(filteredSongs);
                break;

            case SongSortMode.Stars:
                sortedSongs = SortStars(filteredSongs);
                break;

            case SongSortMode.Difficulty:
                sortedSongs = SortDifficulty(filteredSongs);
                break;

            case SongSortMode.Random:
                sortedSongs = SortRandom(filteredSongs);
                break;

            case SongSortMode.Default:
            default:
                sortedSongs = SortSongName(filteredSongs);
                break;
            }

            if (this.Settings.invertSortResults && _settings.sortMode != SongSortMode.Random)
            {
                sortedSongs.Reverse();
            }

            stopwatch.Stop();
            Logger.Info("Sorting songs took {0}ms", stopwatch.ElapsedMilliseconds);

            // Asterisk the pack name so it is identifable as filtered.
            var packName = levelsViewController.levelPack.packName;

            if (!packName.EndsWith("*") && _settings.filterMode != SongFilterMode.None)
            {
                packName += "*";
            }
            BeatmapLevelPack levelPack = new BeatmapLevelPack(SongBrowserModel.FilteredSongsPackId, packName, levelsViewController.levelPack.coverImage, new BeatmapLevelCollection(sortedSongs.ToArray()));

            levelsViewController.SetData(levelPack);

            //_sortedSongs.ForEach(x => Logger.Debug(x.levelID));
        }
 /// <summary>
 /// Sorting returns original list.
 /// </summary>
 /// <param name="levels"></param>
 /// <returns></returns>
 private List <IPreviewBeatmapLevel> SortOriginal(List <IPreviewBeatmapLevel> levels)
 {
     Logger.Info("Sorting song list as original");
     return(levels);
 }
Пример #29
0
 private void SortOriginal(List <BeatmapLevelSO> levels)
 {
     Logger.Info("Sorting song list as original");
     _sortedSongs = levels;
 }
Пример #30
0
        public bool DeleteSong(Song song)
        {
            bool   zippedSong = false;
            string path       = "";

            CustomLevel level = SongLoader.CustomLevels.FirstOrDefault(x => x.levelID.StartsWith(song.hash));

            if (string.IsNullOrEmpty(song.path))
            {
                if (level != null)
                {
                    path = level.customSongInfo.path;
                }
            }
            else
            {
                path = song.path;
            }

            if (string.IsNullOrEmpty(path))
            {
                return(false);
            }
            if (!Directory.Exists(path))
            {
                return(false);
            }

            if (path.Contains("/.cache/"))
            {
                zippedSong = true;
            }

            if (zippedSong)
            {
                Logger.Log("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.Log("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
            {
                Logger.Log("Deleting \"" + path.Substring(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.Log("Deleting empty folder \"" + path.Substring(0, path.LastIndexOf('/')) + "\"...");
                        Directory.Delete(path.Substring(0, path.LastIndexOf('/')), false);
                    }
                }
                catch
                {
                    Logger.Warning("Unable to delete empty folder!");
                }
            }

            if (level != null)
            {
                SongLoader.Instance.RemoveSongWithLevelID(level.levelID);
            }
            Logger.Log($"{_alreadyDownloadedSongs.RemoveAll(x => x.Compare(song))} song removed");
            return(true);
        }