/// <summary>
 ///
 /// </summary>
 /// <param name="settings"></param>
 /// <exception cref="ArgumentNullException"></exception>
 /// <exception cref="ArgumentException"></exception>
 public BeatSaverFeed(BeatSaverFeedSettings settings)
 {
     if (settings == null)
     {
         throw new ArgumentNullException(nameof(settings),
                                         "settings cannot be null when creating a new BeatSaverFeed.");
     }
     BeatSaverFeedSettings = (BeatSaverFeedSettings)settings.Clone();
     Feed     = BeatSaverFeedSettings.Feed;
     FeedInfo = Feeds[BeatSaverFeedSettings.Feed];
     if ((Feed == BeatSaverFeedName.Search || Feed == BeatSaverFeedName.Author) && !BeatSaverFeedSettings.SearchQuery.HasValue)
     {
         throw new ArgumentException(nameof(settings), $"SearchQuery cannot be null in settings for feed {FeedInfo.DisplayName}.");
     }
     SearchQuery = BeatSaverFeedSettings.SearchQuery;
     if (settings.MaxPages > 0)
     {
         if (settings.StartingPage == 1)
         {
             configuredLastPage = settings.MaxPages;
         }
         else
         {
             configuredLastPage = settings.StartingPage + settings.MaxPages - 1;
         }
     }
 }
Пример #2
0
        /// <summary>
        /// Searches for songs with the specified criteria and search type and returns them in a FeedResult.
        /// TODO: PageResults is always an empty array.
        /// </summary>
        /// <param name="criteria"></param>
        /// <param name="type"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public static async Task <FeedResult> SearchAsync(BeatSaverFeedSettings settings, CancellationToken cancellationToken)
        {
            if (settings.SearchType == BeatSaverSearchType.key)
            {
                var songDict = new Dictionary <string, ScrapedSong>();
                var song     = await GetSongByKeyAsync(settings.Criteria, cancellationToken).ConfigureAwait(false);

                if (!string.IsNullOrEmpty(song?.Hash))
                {
                    songDict.Add(song.Hash, song);
                }
                return(new FeedResult(songDict, null));
            }

            if (settings.SearchType == BeatSaverSearchType.user)
            {
                var authorNames = await GetAuthorNamesByIDAsync(settings.Criteria, cancellationToken).ConfigureAwait(false);

                return(await GetSongsByUploaderIdAsync(authorNames.FirstOrDefault(), cancellationToken).ConfigureAwait(false));
            }

            if (settings.SearchType == BeatSaverSearchType.hash)
            {
                var songDict = new Dictionary <string, ScrapedSong>();
                var result   = await GetSongByHashAsync(settings.Criteria, cancellationToken).ConfigureAwait(false);

                var song = result.Songs.FirstOrDefault();
                if (!string.IsNullOrEmpty(song?.Hash))
                {
                    songDict.Add(song.Hash, song);
                }
                return(new FeedResult(songDict, new List <PageReadResult>(1)
                {
                    result
                }));
            }
            StringBuilder url;
            int           maxSongs = 0;
            int           maxPages = 0;
            //int lastPage;
            //int nextPage;
            int pageIndex = 0;

            if (settings != null)
            {
                maxSongs  = settings.MaxSongs;
                maxPages  = settings.MaxPages;
                pageIndex = Math.Max(settings.StartingPage - 1, 0);
            }
            bool useMaxPages = maxPages > 0;
            bool useMaxSongs = maxSongs > 0;

            if (useMaxPages && pageIndex > 0)
            {
                maxPages = maxPages + pageIndex;
            }
            bool                  continueLooping = true;
            var                   songs           = new Dictionary <string, ScrapedSong>();
            PageReadResult        newSongs;
            List <PageReadResult> pageResults = new List <PageReadResult>();

            do
            {
                url = new StringBuilder(Feeds[BeatSaverFeed.Search].BaseUrl);
                url.Replace(SEARCHTYPEKEY, settings.SearchType == BeatSaverSearchType.all ? "text" : "advanced");
                url.Replace(SEARCHQUERY, settings.Criteria);
                url.Replace(PAGEKEY, pageIndex.ToString());
                var uri = new Uri(url.ToString());
                newSongs = await GetSongsFromPageAsync(uri, cancellationToken).ConfigureAwait(false);

                if (!useMaxSongs || songs.Count < maxSongs)
                {
                    pageResults.Add(newSongs);
                }
                foreach (var song in newSongs.Songs)
                {
                    if (songs.ContainsKey(song.Hash))
                    {
                        continue;
                    }
                    if (!useMaxSongs || songs.Count < maxSongs)
                    {
                        songs.Add(song.Hash, song);
                    }
                }
                pageIndex++;
                if (newSongs.Count == 0)
                {
                    continueLooping = false;
                }
                if (useMaxPages && (pageIndex >= maxPages))
                {
                    continueLooping = false;
                }
                if (useMaxSongs && pageIndex * SongsPerPage >= maxSongs)
                {
                    continueLooping = false;
                }
            } while (continueLooping);

            return(new FeedResult(songs, pageResults));
        }
Пример #3
0
 public static FeedResult Search(BeatSaverFeedSettings settings, CancellationToken cancellationToken)
 {
     return(SearchAsync(settings, cancellationToken).Result);
 }
Пример #4
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="settings"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">Thrown when settings is null.</exception>
        public static async Task <FeedResult> GetBeatSaverSongsAsync(BeatSaverFeedSettings settings, CancellationToken cancellationToken)
        {
            if (settings == null)
            {
                throw new ArgumentNullException(nameof(settings), "settings cannot be null for BeatSaverReader.GetBeatSaverSongsAsync");
            }
            if (cancellationToken.IsCancellationRequested)
            {
                return(FeedResult.CancelledResult);
            }
            // TODO: double checks the first page
            int  feedIndex   = settings.FeedIndex;
            bool useMaxPages = settings.MaxPages != 0;
            bool useMaxSongs = settings.MaxSongs != 0;
            Dictionary <string, ScrapedSong> songs = new Dictionary <string, ScrapedSong>();
            string pageText = string.Empty;

            JObject             result   = new JObject();
            var                 pageUri  = GetPageUrl(feedIndex);
            IWebResponseMessage response = null;

            try
            {
                response = await GetBeatSaverAsync(pageUri, cancellationToken).ConfigureAwait(false);

                response.EnsureSuccessStatusCode();
                pageText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                result = JObject.Parse(pageText);
            }
            catch (WebClientException ex)
            {
                string errorText = string.Empty;
                if (ex.Response != null)
                {
                    switch (ex.Response.StatusCode)
                    {
                    case 408:
                        errorText = "Timeout";
                        break;

                    default:
                        errorText = "Site Error";
                        break;
                    }
                }
                string message = $"{errorText} getting a response from {pageUri}: {ex.Message}";
                Logger?.Debug(message);
                Logger?.Debug($"{ex.Message}\n{ex.StackTrace}");
                return(new FeedResult(null, null, new FeedReaderException(message, ex, FeedReaderFailureCode.SourceFailed), FeedResultError.Error));
            }
            catch (JsonReaderException ex)
            {
                string message = "Unable to parse JSON from text on first page in GetBeatSaverSongAsync()";
                Logger?.Debug(message);
                Logger?.Debug($"{ex.Message}\n{ex.StackTrace}");
                return(new FeedResult(null, null, new FeedReaderException(message, ex, FeedReaderFailureCode.SourceFailed), FeedResultError.Error));
            }
            catch (OperationCanceledException)
            {
                return(FeedResult.CancelledResult);
            }
            catch (Exception ex)
            {
                string message = $"Uncaught error getting the first page in BeatSaverReader.GetBeatSaverSongsAsync(): {ex.Message}";
                return(new FeedResult(null, null, new FeedReaderException(message, ex, FeedReaderFailureCode.SourceFailed), FeedResultError.Error));
            }
            finally
            {
                response?.Dispose();
                response = null;
            }
            int?numSongs = result["totalDocs"]?.Value <int>();
            int?lastPage = result["lastPage"]?.Value <int>();

            if (numSongs == null || lastPage == null || numSongs == 0)
            {
                Logger?.Warning($"Error checking Beat Saver's {settings.FeedName} feed.");
                return(new FeedResult(null, null, new FeedReaderException($"Error getting the first page in GetBeatSaverSongsAsync()", null, FeedReaderFailureCode.SourceFailed), FeedResultError.Error));
            }
            Logger?.Info($"Checking Beat Saver's {settings.FeedName} feed, {numSongs} songs available");
            int maxPages = settings.MaxPages;
            int pageNum  = Math.Max(settings.StartingPage - 1, 0);

            if (pageNum > 0 && useMaxPages)
            {
                maxPages += pageNum; // Add starting page to maxPages so we actually get songs if maxPages < starting page
            }
            List <Task <PageReadResult> > pageReadTasks = new List <Task <PageReadResult> >();
            bool continueLooping = true;

            do
            {
                pageUri = GetPageUrl(feedIndex, pageNum);
                Logger?.Trace($"Creating task for {pageUri.ToString()}");
                pageReadTasks.Add(GetSongsFromPageAsync(pageUri, cancellationToken));
                pageNum++;
                if ((pageNum > lastPage))
                {
                    continueLooping = false;
                }
                if (useMaxPages && (pageNum >= maxPages))
                {
                    continueLooping = false;
                }
                if (useMaxSongs && pageNum * SongsPerPage >= settings.MaxSongs)
                {
                    continueLooping = false;
                }
            } while (continueLooping);
            try
            {
                // TODO: Won't hit max songs if a song is added while reading the pages. (It'll bump all the songs down and we'll get a repeat)
                await Task.WhenAll(pageReadTasks.ToArray()).ConfigureAwait(false);
            }
#pragma warning disable CA1031 // Do not catch general exception types
            catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
            {
                string message = $"Error waiting for pageReadTasks: {ex.Message}";
                // TODO: Probably don't need logging here.
                Logger?.Debug(message);
                Logger?.Debug(ex.StackTrace);
                return(new FeedResult(null, null, new FeedReaderException(message, ex, FeedReaderFailureCode.SourceFailed), FeedResultError.Error));
            }
            var pageResults = new List <PageReadResult>();
            foreach (var job in pageReadTasks)
            {
                var pageResult = await job.ConfigureAwait(false);

                pageResults.Add(pageResult);
                foreach (var song in pageResult.Songs)
                {
                    if (songs.ContainsKey(song.Hash))
                    {
                        continue;
                    }
                    if (!useMaxSongs || songs.Count < settings.MaxSongs)
                    {
                        songs.Add(song.Hash, song);
                    }
                }
            }
            return(new FeedResult(songs, pageResults));
        }
Пример #5
0
 public static FeedResult GetBeatSaverSongs(BeatSaverFeedSettings settings)
 {
     return(GetBeatSaverSongsAsync(settings, CancellationToken.None).Result);
 }