protected override void DidActivate(bool firstActivation, ActivationType activationType)
        {
            try
            {
                if (firstActivation && activationType == ActivationType.AddedToHierarchy)
                {
                    title = "Playlists";

                    if (_playlistsReader == null)
                    {
                        _playlistsReader = new PlaylistsReader();
                        _playlistsReader.UpdatePlaylists();
                        Logger.Debug("Reader found {0} playlists!", _playlistsReader.Playlists.Count);

                        this.MatchSongsForAllPlaylists(true);
                    }

                    _playlistsNavigationController.didFinishEvent += _playlistsNavigationController_didFinishEvent;

                    _playlistListViewController = BeatSaberUI.CreateViewController <PlaylistListViewController>();
                    _playlistListViewController.didSelectRow += _playlistListViewController_didSelectRow;

                    _playlistDetailViewController.downloadButtonPressed += _playlistDetailViewController_downloadButtonPressed;
                    _playlistDetailViewController.selectButtonPressed   += _playlistDetailViewController_selectButtonPressed;

                    _downloadQueueViewController = BeatSaberUI.CreateViewController <DownloadQueueViewController>();

                    SetViewControllersToNavigationConctroller(_playlistsNavigationController, new VRUIViewController[]
                    {
                        _playlistListViewController
                    });

                    ProvideInitialViewControllers(_playlistsNavigationController, _downloadQueueViewController, null);
                }
                _downloadingPlaylist = false;
                _playlistListViewController.SetContent(_playlistsReader.Playlists);

                _downloadQueueViewController.allSongsDownloaded += _downloadQueueViewController_allSongsDownloaded;
            }
            catch (Exception e)
            {
                Logger.Exception("Error activating playlist flow coordinator: ", e);
            }
        }
        /// <summary>
        /// Get the song cache from the game.
        /// </summary>
        public void UpdateSongLists(BeatmapCharacteristicSO gameplayMode)
        {
            // give up
            if (gameplayMode == null)
            {
                Logger.Debug("Always null first time if user waits for SongLoader event, which they should...");
                return;
            }

            Stopwatch timer = new Stopwatch();

            timer.Start();

            // Get the level collection from song loader
            LevelCollectionSO levelCollections = Resources.FindObjectsOfTypeAll <LevelCollectionSO>().FirstOrDefault();

            // Stash everything we need
            _originalSongs = levelCollections.GetLevelsWithBeatmapCharacteristic(gameplayMode).ToList();

            Logger.Debug("Got {0} songs from level collections...", _originalSongs.Count);
            //_originalSongs.ForEach(x => Logger.Debug("{0} by {1} = {2}", x.name, x.levelAuthorName, x.levelID));

            _sortedSongs = _originalSongs;
            CurrentBeatmapCharacteristicSO = gameplayMode;

            // 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;
            }

            // This operation scales well
            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)
                {
                    _cachedLastWriteTimes[level.levelID] = (File.GetLastWriteTimeUtc(level.customSongInfo.path) - 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))
                    {
                        //Logger.Debug("MATCH");
                        _levelIdToSongVersion.Add(level.levelID, 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();
            this.UpdateDirectoryTree(customSongsPath);

            // 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 (!String.IsNullOrEmpty(this.Settings.currentEditingPlaylistFile))
            {
                CurrentEditingPlaylist = PlaylistsReader.ParsePlaylist(this.Settings.currentEditingPlaylistFile);
            }

            if (CurrentEditingPlaylist == null)
            {
                CurrentEditingPlaylist = new Playlist
                {
                    Title  = "Song Browser Favorites",
                    Author = "SongBrowserPlugin",
                    Path   = this.Settings.currentEditingPlaylistFile,
                    Image  = Base64Sprites.PlaylistIconB64,
                    Songs  = new List <PlaylistSong>(),
                };
            }

            CurrentEditingPlaylistLevelIds = new HashSet <string>();
            foreach (PlaylistSong ps in CurrentEditingPlaylist.Songs)
            {
                CurrentEditingPlaylistLevelIds.Add(ps.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);
            Logger.Debug("Song Browser knows about {0} songs from SongLoader...", _originalSongs.Count);
        }