public async Task <IEnumerable <LastFmAlbum> > GetTopAlbums(LastFmDataPeriod period, int count) { if (period == LastFmDataPeriod.Day) { var tracks = await GetRecentTracks(StatsPeriodTimeMapping[period]); return(tracks .SkipWhile(x => x.NowPlaying) .GroupBy(x => x.Album.Id) .Select(x => x.First().Album.WithPlaycount(x.Count())) .OrderByDescending(x => x.Playcount)); } else { var request = WebRequest.CreateHttp($"{ApiBase}/?method=user.gettopalbums&user={User}&api_key={Key}&period={StatsPeriodMapping[period]}&limit={count}&format=json"); request.Timeout = (int)RequestTimeout.TotalMilliseconds; using (var response = (HttpWebResponse)await request.GetResponseAsync()) using (var reader = new StreamReader(response.GetResponseStream())) { var text = await reader.ReadToEndAsync(); var root = JObject.Parse(text); var results = root?["topalbums"]?["album"] as JArray ?? Enumerable.Empty <JToken>(); return(results.Select(x => new LastFmAlbum((string)x["name"], new LastFmArtist((string)x["artist"]["name"]), (int)x["playcount"]))); } } }
public async Task <IEnumerable <LastFmScore <LastFmTrack> > > GetTrackScores(LastFmDataPeriod period, int count, Task <int> totalPlaycount) { var items = await GetTopTracks(period, count); var playcount = await totalPlaycount; return(items.Select(x => new LastFmScore <LastFmTrack>(x, (double)x.Playcount / playcount))); }
public async Task <IEnumerable <LastFmTrack> > GetTopTracks(LastFmDataPeriod period, int count = int.MaxValue, CancellationToken ct = default) { if (period == LastFmDataPeriod.Day) { var tracks = await GetRecentTracks(StatsPeriodTimeMapping[period]); return(tracks .SkipWhile(x => x.NowPlaying) .GroupBy(x => x.Id) .Select(x => x.First().ToTrack(x.Count())) .OrderByDescending(x => x.Playcount)); } else { var retrieved = 0; var page = 1; var results = Enumerable.Empty <LastFmTrack>(); var pageSize = Math.Min(count, MaxTopPageSize); while (retrieved < count) { ct.ThrowIfCancellationRequested(); var request = WebRequest.CreateHttp($"{ApiBase}/?method=user.gettoptracks&user={User}&api_key={Key}&period={StatsPeriodMapping[period]}&limit={pageSize}&page={page}&format=json"); request.Timeout = (int)RequestTimeout.TotalMilliseconds; using (var response = (HttpWebResponse)await request.GetResponseAsync()) using (var reader = new StreamReader(response.GetResponseStream())) { ct.ThrowIfCancellationRequested(); var text = await reader.ReadToEndAsync(); ct.ThrowIfCancellationRequested(); var root = JObject.Parse(text); var pageResults = (root?["toptracks"]?["track"] as JArray ?? Enumerable.Empty <JToken>()).ToList(); results = results.Concat(pageResults.Select(x => new LastFmTrack((string)x["name"], new LastFmAlbum(null, new LastFmArtist((string)x["artist"]["name"]), null), (int)x["playcount"]))); retrieved += pageResults.Count; var more = page < ((int?)root?["toptracks"]?["@attr"]?["totalPages"] ?? 1); if (!more || !pageResults.Any()) { break; } } page++; } return(results); } }
private string FormatStatsDataPeriod(LastFmDataPeriod period) { switch (period) { case LastFmDataPeriod.Day: return("data from the last 24 hours"); case LastFmDataPeriod.Week: return("data from the last week"); case LastFmDataPeriod.Month: return("data from the last month"); case LastFmDataPeriod.QuarterYear: return("data from the last 3 months"); case LastFmDataPeriod.HalfYear: return("data from the last 6 months"); case LastFmDataPeriod.Year: return("data from the last year"); case LastFmDataPeriod.Overall: return("all data"); default: throw new ArgumentException($"Unknown value {period}"); } }
public Task <IEnumerable <LastFmRecentTrack> > GetRecentTracks(LastFmDataPeriod period, int count = int.MaxValue) => period == LastFmDataPeriod.Overall ? GetRecentTracks(count: count) : GetRecentTracks(StatsPeriodTimeMapping[period], count);
public async Task <LastFmArtistDetail> GetArtistDetail(string artist, LastFmDataPeriod period) { try { var infoTask = GetArtistInfo(artist); var request = WebRequest.CreateHttp($"https://www.last.fm/user/{User}/library/music/{Uri.EscapeDataString(artist)}?date_preset={StatsPeriodWebMapping[period]}"); request.Timeout = (int)RequestTimeout.TotalMilliseconds; using (var response = (HttpWebResponse)await request.GetResponseAsync()) using (var reader = new StreamReader(response.GetResponseStream())) { var content = await reader.ReadToEndAsync(); var doc = new HtmlDocument(); doc.LoadHtml(content); // Get artist info var info = await infoTask; if (info == null) { return(null); } // Get avatar (we can't get it via the API because it never actually has the artist's image...) var image = doc.DocumentNode.Descendants("span") .FirstOrDefault(x => x.HasClass("library-header-image"))? .Descendants("img") .FirstOrDefault()? .GetAttributeValue("src", null)? .Replace("avatar70s", "avatar170s"); // Get playcount and count of listened albums and tracks (in that order) var metadata = doc.DocumentNode .Descendants("p") .Where(x => x.HasClass("metadata-display") && x.ParentNode.HasClass("metadata-item")) .Select(x => int.Parse(x.InnerText, System.Globalization.NumberStyles.Any, new System.Globalization.CultureInfo("en-US"))) .ToList(); var playcount = metadata.ElementAtOrDefault(0); var albumsListened = metadata.ElementAtOrDefault(1); var tracksListened = metadata.ElementAtOrDefault(2); // Get top albums and tracks IEnumerable <(string name, string url, int playcount)> GetTopListItems(HtmlNode topList) { foreach (var item in topList.Descendants("tr").Where(x => x.HasClass("chartlist-row") || x.HasClass("chartlist-row\n"))) { var itemNameLink = item.Descendants("td").First(x => x.HasClass("chartlist-name")).Descendants("a").First(); var itemUrl = "https://www.last.fm" + itemNameLink.GetAttributeValue("href", null); var scrobbleText = item.Descendants("span").First(x => x.HasClass("chartlist-count-bar-value")).InnerText; var scrobbleCount = int.Parse(Regex.Match(scrobbleText, @"[\d,.\s]+").Value, System.Globalization.NumberStyles.Any, new System.Globalization.CultureInfo("en-US")); yield return(WebUtility.HtmlDecode(itemNameLink.InnerText), itemUrl, scrobbleCount); } } var topLists = doc.DocumentNode.Descendants("table").Where(x => x.HasClass("chartlist") || x.HasClass("chartlist\n")).ToList(); var topAlbums = Enumerable.Empty <LastFmAlbum>(); if (topLists.Any()) { topAlbums = GetTopListItems(topLists.First()).Select(x => new LastFmAlbum(x.name, info, x.playcount)); } var topTracks = Enumerable.Empty <LastFmTrack>(); if (topLists.Skip(1).Any()) { topTracks = GetTopListItems(topLists.Skip(1).First()).Select(x => new LastFmTrack(x.name, new LastFmAlbum(null, info), x.playcount)); } var imageUri = string.IsNullOrEmpty(image) ? null : new Uri(image); return(new LastFmArtistDetail(info, imageUri, topAlbums, topTracks, albumsListened, tracksListened, playcount)); } } catch (WebException e) when((e.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) { return(null); } }
public Task <int> GetTotalPlaycount(LastFmDataPeriod period) => period == LastFmDataPeriod.Overall ? GetTotalPlaycount() : GetTotalPlaycount(StatsPeriodTimeMapping[period]);