/// <summary>
        /// Creates a SongInfo from a JObject.
        /// </summary>
        /// <param name="song"></param>
        /// <exception cref="ArgumentException">Thrown when a hash can't be found for the given song JObject.</exception>
        /// <returns></returns>
        public static ScrapedSong ParseSongFromJson(JObject song, Uri sourceUrl)
        {
            if (song == null)
            {
                throw new ArgumentNullException(nameof(song), "song cannot be null for BeatSaverReader.ParseSongFromJson.");
            }
            //JSONObject song = (JSONObject) aKeyValue;
            string songKey    = song["key"]?.Value <string>();
            string songHash   = song["hash"]?.Value <string>().ToUpper();
            var    songName   = song["name"]?.Value <string>();
            var    mapperName = song["uploader"]?["username"]?.Value <string>();

            if (string.IsNullOrEmpty(songHash))
            {
                throw new ArgumentException("Unable to find hash for the provided song, is this a valid song JObject?");
            }
            string downloadUri = !string.IsNullOrEmpty(songKey) ? BEATSAVER_DOWNLOAD_URL_BASE + songKey : string.Empty;
            var    newSong     = new ScrapedSong(songHash)
            {
                DownloadUri = Util.GetUriFromString(downloadUri),
                SourceUri   = sourceUrl,
                SongName    = songName,
                MapperName  = mapperName,
                RawData     = song.ToString()
            };

            return(newSong);
        }
        public static async Task <ScrapedSong> GetSongByKeyAsync(string key)
        {
            var         uri      = new Uri(BEATSAVER_DETAILS_BASE_URL + key);
            string      pageText = "";
            ScrapedSong song     = null;

            try
            {
                using (var response = await WebUtils.WebClient.GetAsync(uri).ConfigureAwait(false))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        pageText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                    }
                    else
                    {
                        Logger.Error($"Error getting song by key, {uri} responded with {response.StatusCode}:{response.ReasonPhrase}");
                        return(song);
                    }
                }
                if (string.IsNullOrEmpty(pageText))
                {
                    Logger.Warning($"Unable to get web page at {uri}");
                    return(null);
                }
            }
            catch (HttpRequestException)
            {
                Logger.Error($"HttpRequestException while trying to populate fields for {key}");
                return(null);
            }
            catch (AggregateException ae)
            {
                ae.WriteExceptions($"Exception while trying to get details for {key}");
            }
            catch (Exception ex)
            {
                Logger.Exception($"Exception getting page {uri}", ex);
                throw;
            }
            song = ParseSongsFromPage(pageText, uri).FirstOrDefault();
            return(song);
        }