diff --git a/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetTopArtistsCommandTests.cs b/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetTopArtistsCommandTests.cs new file mode 100644 index 0000000..496eba4 --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Api/Commands/UserGetTopArtistsCommandTests.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using IF.Lastfm.Core.Api.Commands.User; +using IF.Lastfm.Core.Api.Enums; +using IF.Lastfm.Core.Api.Helpers; +using IF.Lastfm.Core.Objects; +using IF.Lastfm.Core.Tests.Resources; +using NUnit.Framework; + +namespace IF.Lastfm.Core.Tests.Api.Commands +{ + public class UserGetTopArtistsCommandTests : CommandTestsBase + { + private const string USER = "Aurvandil"; + private const LastStatsTimeSpan SPAN = LastStatsTimeSpan.Overall; + + [Test] + public void ParametersCorrect() + { + var expected = new Dictionary + { + {"user", USER}, + {"period", SPAN.GetApiName()}, + {"page", "5"}, + {"limit", "10"} + }; + + var command = new GetTopArtistsCommand(MAuth.Object, USER, SPAN) + { + Page = 5, + Count = 10 + }; + + command.SetParameters(); + command.Parameters.Remove("disablecachetoken"); + + TestHelper.AssertSerialiseEqual(expected, command.Parameters); + } + + [Test] + public async Task HandleResponseSingle() + { + var command = new GetTopArtistsCommand(MAuth.Object, USER, SPAN) + { + Page = 1, + Count = 1 + }; + + command.SetParameters(); + + var expectedArtist = new LastArtist + { + Name = "Anathema", + PlayCount = 5216, + Mbid = "20aa23e3-3532-42ca-acf6-e8c2e9df2688", + Url = new Uri("http://www.last.fm/music/Anathema"), + MainImage = + new LastImageSet("http://userserve-ak.last.fm/serve/34/12571597.jpg", + "http://userserve-ak.last.fm/serve/64/12571597.jpg", + "http://userserve-ak.last.fm/serve/126/12571597.jpg", + "http://userserve-ak.last.fm/serve/252/12571597.jpg", + "http://userserve-ak.last.fm/serve/_/12571597/Anathema+Judgement+promo.jpg") + }; + + var response = CreateResponseMessage(Encoding.UTF8.GetString(UserApiResponses.UserGetTopArtistsSingle)); + var parsed = await command.HandleResponse(response); + + Assert.IsTrue(parsed.Success); + Assert.AreEqual(1, parsed.Page); + Assert.AreEqual(1, parsed.PageSize); + Assert.AreEqual(1124, parsed.TotalItems); + Assert.AreEqual(1124, parsed.TotalPages); + Assert.AreEqual(1, parsed.Content.Count); + + var actualArtist = parsed.Content.First(); + + TestHelper.AssertSerialiseEqual(expectedArtist, actualArtist); + } + + [Test] + public async Task HandleResponseMultiple() + { + var command = new GetTopArtistsCommand(MAuth.Object, USER, SPAN) + { + Page = 1, + Count = 2 + }; + + command.SetParameters(); + + var expectedArtists = new List + { + new LastArtist + { + Name = "Anathema", + PlayCount = 5216, + Mbid = "20aa23e3-3532-42ca-acf6-e8c2e9df2688", + Url = new Uri("http://www.last.fm/music/Anathema"), + MainImage = + new LastImageSet("http://userserve-ak.last.fm/serve/34/12571597.jpg", + "http://userserve-ak.last.fm/serve/64/12571597.jpg", + "http://userserve-ak.last.fm/serve/126/12571597.jpg", + "http://userserve-ak.last.fm/serve/252/12571597.jpg", + "http://userserve-ak.last.fm/serve/_/12571597/Anathema+Judgement+promo.jpg") + }, + new LastArtist + { + Name = "Insomnium", + PlayCount = 4670, + Mbid = "c1f8e226-75ea-4fe6-83ce-59c122bcbca4", + Url = new Uri("http://www.last.fm/music/Insomnium"), + MainImage = + new LastImageSet("http://userserve-ak.last.fm/serve/34/70409268.jpg", + "http://userserve-ak.last.fm/serve/64/70409268.jpg", + "http://userserve-ak.last.fm/serve/126/70409268.jpg", + "http://userserve-ak.last.fm/serve/252/70409268.jpg", + "http://userserve-ak.last.fm/serve/500/70409268/Insomnium.jpg") + }, + }; + + var response = CreateResponseMessage(Encoding.UTF8.GetString(UserApiResponses.UserGetTopArtistsMultiple)); + var parsed = await command.HandleResponse(response); + + Assert.IsTrue(parsed.Success); + Assert.AreEqual(1, parsed.Page); + Assert.AreEqual(2, parsed.PageSize); + Assert.AreEqual(1124, parsed.TotalItems); + Assert.AreEqual(562, parsed.TotalPages); + Assert.AreEqual(2, parsed.Content.Count); + + var actualArtists = parsed.Content; + + TestHelper.AssertSerialiseEqual(expectedArtists, actualArtists); + } + + [Test] + public async Task HandleResponseEmpty() + { + const string USER_WITH_NO_PLAYS = "e"; + var command = new GetTopArtistsCommand(MAuth.Object, USER_WITH_NO_PLAYS, SPAN) + { + Page = 1, + Count = 1 + }; + + command.SetParameters(); + + var response = CreateResponseMessage(Encoding.UTF8.GetString(UserApiResponses.UserGetTopArtistsEmpty)); + var parsed = await command.HandleResponse(response); + + Assert.IsTrue(parsed.Success); + Assert.AreEqual(1, parsed.Page); + Assert.AreEqual(0, parsed.PageSize); + Assert.AreEqual(0, parsed.TotalItems); + Assert.AreEqual(1, parsed.TotalPages); + Assert.AreEqual(0, parsed.Content.Count); + } + + [Test] + public async Task HandleResponseError() + { + var command = new GetTopArtistsCommand(MAuth.Object, USER, SPAN) + { + Page = 1, + Count = 1 + }; + + command.SetParameters(); + + var response = CreateResponseMessage(Encoding.UTF8.GetString(UserApiResponses.UserGetTopArtistsError)); + var parsed = await command.HandleResponse(response); + + Assert.IsFalse(parsed.Success); + Assert.AreEqual(1, parsed.Page); + Assert.AreEqual(0, parsed.PageSize); + Assert.AreEqual(0, parsed.TotalItems); + Assert.AreEqual(1, parsed.TotalPages); + Assert.AreEqual(0, parsed.Content.Count); + Assert.AreEqual(LastResponseStatus.MissingParameters, parsed.Status); + } + } +} diff --git a/src/IF.Lastfm.Core.Tests/IF.Lastfm.Core.Tests.csproj b/src/IF.Lastfm.Core.Tests/IF.Lastfm.Core.Tests.csproj index f39dc14..08040d8 100644 --- a/src/IF.Lastfm.Core.Tests/IF.Lastfm.Core.Tests.csproj +++ b/src/IF.Lastfm.Core.Tests/IF.Lastfm.Core.Tests.csproj @@ -99,6 +99,7 @@ + @@ -195,6 +196,10 @@ + + + + diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsEmpty.json b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsEmpty.json new file mode 100644 index 0000000..1a6458c --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsEmpty.json @@ -0,0 +1,11 @@ +{ + "topartists": { + "#text": "\n ", + "user": "e", + "type": "overall", + "page": "0", + "perPage": "1", + "totalPages": "0", + "total": "0" + } +} \ No newline at end of file diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsError.json b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsError.json new file mode 100644 index 0000000..b1579f5 --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsError.json @@ -0,0 +1,5 @@ +{ + "error": 6, + "message": "No user with that name", + "links": [] +} \ No newline at end of file diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsMultiple.json b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsMultiple.json new file mode 100644 index 0000000..e80e40f --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsMultiple.json @@ -0,0 +1,78 @@ +{ + "topartists": { + "artist": [ + { + "name": "Anathema", + "playcount": "5216", + "mbid": "20aa23e3-3532-42ca-acf6-e8c2e9df2688", + "url": "http://www.last.fm/music/Anathema", + "streamable": "0", + "image": [ + { + "#text": "http://userserve-ak.last.fm/serve/34/12571597.jpg", + "size": "small" + }, + { + "#text": "http://userserve-ak.last.fm/serve/64/12571597.jpg", + "size": "medium" + }, + { + "#text": "http://userserve-ak.last.fm/serve/126/12571597.jpg", + "size": "large" + }, + { + "#text": "http://userserve-ak.last.fm/serve/252/12571597.jpg", + "size": "extralarge" + }, + { + "#text": "http://userserve-ak.last.fm/serve/_/12571597/Anathema+Judgement+promo.jpg", + "size": "mega" + } + ], + "@attr": { + "rank": "1" + } + }, + { + "name": "Insomnium", + "playcount": "4670", + "mbid": "c1f8e226-75ea-4fe6-83ce-59c122bcbca4", + "url": "http://www.last.fm/music/Insomnium", + "streamable": "0", + "image": [ + { + "#text": "http://userserve-ak.last.fm/serve/34/70409268.jpg", + "size": "small" + }, + { + "#text": "http://userserve-ak.last.fm/serve/64/70409268.jpg", + "size": "medium" + }, + { + "#text": "http://userserve-ak.last.fm/serve/126/70409268.jpg", + "size": "large" + }, + { + "#text": "http://userserve-ak.last.fm/serve/252/70409268.jpg", + "size": "extralarge" + }, + { + "#text": "http://userserve-ak.last.fm/serve/500/70409268/Insomnium.jpg", + "size": "mega" + } + ], + "@attr": { + "rank": "2" + } + } + ], + "@attr": { + "user": "Aurvandil", + "type": "overall", + "page": "1", + "perPage": "2", + "totalPages": "562", + "total": "1124" + } + } +} diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsSingle.json b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsSingle.json new file mode 100644 index 0000000..b837a41 --- /dev/null +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApi/UserGetTopArtistsSingle.json @@ -0,0 +1,44 @@ +{ + "topartists": { + "artist": { + "name": "Anathema", + "playcount": "5216", + "mbid": "20aa23e3-3532-42ca-acf6-e8c2e9df2688", + "url": "http://www.last.fm/music/Anathema", + "streamable": "0", + "image": [ + { + "#text": "http://userserve-ak.last.fm/serve/34/12571597.jpg", + "size": "small" + }, + { + "#text": "http://userserve-ak.last.fm/serve/64/12571597.jpg", + "size": "medium" + }, + { + "#text": "http://userserve-ak.last.fm/serve/126/12571597.jpg", + "size": "large" + }, + { + "#text": "http://userserve-ak.last.fm/serve/252/12571597.jpg", + "size": "extralarge" + }, + { + "#text": "http://userserve-ak.last.fm/serve/_/12571597/Anathema+Judgement+promo.jpg", + "size": "mega" + } + ], + "@attr": { + "rank": "1" + } + }, + "@attr": { + "user": "Aurvandil", + "type": "overall", + "page": "1", + "perPage": "1", + "totalPages": "1124", + "total": "1124" + } + } +} \ No newline at end of file diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.Designer.cs b/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.Designer.cs index c9114d1..eeb31b9 100644 --- a/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.Designer.cs +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.0 +// Runtime Version:4.0.30319.34014 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -169,5 +169,45 @@ public static byte[] UserGetTopAlbumsSingle { return ((byte[])(obj)); } } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + public static byte[] UserGetTopArtistsEmpty { + get { + object obj = ResourceManager.GetObject("UserGetTopArtistsEmpty", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + public static byte[] UserGetTopArtistsError { + get { + object obj = ResourceManager.GetObject("UserGetTopArtistsError", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + public static byte[] UserGetTopArtistsMultiple { + get { + object obj = ResourceManager.GetObject("UserGetTopArtistsMultiple", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + public static byte[] UserGetTopArtistsSingle { + get { + object obj = ResourceManager.GetObject("UserGetTopArtistsSingle", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.resx b/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.resx index 798b34c..f080b31 100644 --- a/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.resx +++ b/src/IF.Lastfm.Core.Tests/Resources/UserApiResponses.resx @@ -151,4 +151,16 @@ userapi\usergettopalbumssingle.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + UserApi\UserGetTopArtistsEmpty.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UserApi\UserGetTopArtistsError.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UserApi\UserGetTopArtistsMultiple.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + UserApi\UserGetTopArtistsSingle.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/src/IF.Lastfm.Core/Api/Commands/User/GetTopArtistsCommand.cs b/src/IF.Lastfm.Core/Api/Commands/User/GetTopArtistsCommand.cs new file mode 100644 index 0000000..4771074 --- /dev/null +++ b/src/IF.Lastfm.Core/Api/Commands/User/GetTopArtistsCommand.cs @@ -0,0 +1,51 @@ +using System.Net.Http; +using System.Threading.Tasks; +using IF.Lastfm.Core.Api.Enums; +using IF.Lastfm.Core.Api.Helpers; +using IF.Lastfm.Core.Objects; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace IF.Lastfm.Core.Api.Commands.User +{ + [ApiMethodName("user.getTopArtists")] + internal class GetTopArtistsCommand : GetAsyncCommandBase> + { + public string Username { get; set; } + public LastStatsTimeSpan TimeSpan { get; set; } + + public GetTopArtistsCommand(ILastAuth auth, string username, LastStatsTimeSpan span) + : base(auth) + { + Username = username; + TimeSpan = span; + } + + public override void SetParameters() + { + Parameters.Add("user", Username); + Parameters.Add("period", TimeSpan.GetApiName()); + + AddPagingParameters(); + DisableCaching(); + } + + public async override Task> HandleResponse(HttpResponseMessage response) + { + var json = await response.Content.ReadAsStringAsync(); + + LastResponseStatus status; + if (LastFm.IsResponseValid(json, out status) && response.IsSuccessStatusCode) + { + var jtoken = JsonConvert.DeserializeObject(json); + var topArtistsToken = jtoken.SelectToken("topartists"); + var itemsToken = topArtistsToken.SelectToken("artist"); + var pageInfoToken = topArtistsToken.SelectToken("@attr"); + + return PageResponse.CreateSuccessResponse(itemsToken, pageInfoToken, LastArtist.ParseJToken, LastPageResultsType.Attr); + } + + return LastResponse.CreateErrorResponse>(status); + } + } +} diff --git a/src/IF.Lastfm.Core/Api/IUserApi.cs b/src/IF.Lastfm.Core/Api/IUserApi.cs index 3cd3458..99468b7 100644 --- a/src/IF.Lastfm.Core/Api/IUserApi.cs +++ b/src/IF.Lastfm.Core/Api/IUserApi.cs @@ -19,6 +19,11 @@ Task> GetTopAlbums(string username, int startIndex = 0, int endIndex = LastFm.DefaultPageLength); + Task> GetTopArtists(string username, + LastStatsTimeSpan span, + int pagenumber = 0, + int count = LastFm.DefaultPageLength); + Task> GetRecentScrobbles(string username, DateTimeOffset? since = null, int pagenumber = 0, int count = LastFm.DefaultPageLength); Task> GetRecentStations(string username, diff --git a/src/IF.Lastfm.Core/Api/UserApi.cs b/src/IF.Lastfm.Core/Api/UserApi.cs index 6c0b442..6b3ad88 100644 --- a/src/IF.Lastfm.Core/Api/UserApi.cs +++ b/src/IF.Lastfm.Core/Api/UserApi.cs @@ -42,6 +42,18 @@ public async Task> GetTopAlbums(string username, LastSta return await command.ExecuteAsync(); } + public async Task> GetTopArtists(string username, LastStatsTimeSpan span, int pagenumber = 0, int count = LastFm.DefaultPageLength) + { + var command = new GetTopArtistsCommand(Auth, username, span) + { + Page = pagenumber, + Count = count, + HttpClient = HttpClient + }; + + return await command.ExecuteAsync(); + } + /// /// Gets a list of recent scrobbled tracks for this user in reverse date order. /// diff --git a/src/IF.Lastfm.Core/IF.Lastfm.Core.csproj b/src/IF.Lastfm.Core/IF.Lastfm.Core.csproj index cf5ac41..ea2d58c 100644 --- a/src/IF.Lastfm.Core/IF.Lastfm.Core.csproj +++ b/src/IF.Lastfm.Core/IF.Lastfm.Core.csproj @@ -68,6 +68,7 @@ +