mirror of
https://github.com/Sarsoo/IF.Lastfm.git
synced 2024-10-17 07:13:09 +01:00
Merge pull request #46 from zumicts/master
This commit is contained in:
commit
27f2b6f836
@ -17,8 +17,13 @@ public class TrackScrobbleCommandTests : CommandIntegrationTestsBase
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task ScrobblesSingle()
|
public async Task ScrobblesSingle()
|
||||||
{
|
{
|
||||||
var trackPlayed = DateTime.UtcNow;
|
var trackPlayed = DateTime.UtcNow.AddMinutes(-1);
|
||||||
var testScrobble = new Scrobble("Hot Chip", "The Warning", "Over and Over", trackPlayed, "Hot Chip", false);
|
var testScrobble = new Scrobble("Hot Chip", "The Warning", "Over and Over")
|
||||||
|
{
|
||||||
|
AlbumArtist = ARTIST_NAME,
|
||||||
|
TimePlayed = trackPlayed,
|
||||||
|
ChosenByUser = false
|
||||||
|
};
|
||||||
|
|
||||||
var trackApi = new TrackApi(Auth);
|
var trackApi = new TrackApi(Auth);
|
||||||
var response = await trackApi.ScrobbleAsync(testScrobble);
|
var response = await trackApi.ScrobbleAsync(testScrobble);
|
||||||
@ -26,7 +31,7 @@ public async Task ScrobblesSingle()
|
|||||||
Assert.IsTrue(response.Success);
|
Assert.IsTrue(response.Success);
|
||||||
|
|
||||||
var userApi = new UserApi(Auth);
|
var userApi = new UserApi(Auth);
|
||||||
var tracks = await userApi.GetRecentScrobbles(Auth.UserSession.Username, trackPlayed.AddSeconds(-10), 0, 1);
|
var tracks = await userApi.GetRecentScrobbles(Auth.UserSession.Username, null, 0, 1);
|
||||||
|
|
||||||
var expectedTrack = new LastTrack
|
var expectedTrack = new LastTrack
|
||||||
{
|
{
|
||||||
@ -40,7 +45,7 @@ public async Task ScrobblesSingle()
|
|||||||
"http://userserve-ak.last.fm/serve/64s/50921593.png",
|
"http://userserve-ak.last.fm/serve/64s/50921593.png",
|
||||||
"http://userserve-ak.last.fm/serve/126/50921593.png",
|
"http://userserve-ak.last.fm/serve/126/50921593.png",
|
||||||
"http://userserve-ak.last.fm/serve/300x300/50921593.png"),
|
"http://userserve-ak.last.fm/serve/300x300/50921593.png"),
|
||||||
TimePlayed = trackPlayed
|
TimePlayed = trackPlayed.RoundToNearestSecond()
|
||||||
};
|
};
|
||||||
|
|
||||||
var expectedJson = expectedTrack.TestSerialise();
|
var expectedJson = expectedTrack.TestSerialise();
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using IF.Lastfm.Core.Api;
|
||||||
|
using IF.Lastfm.Core.Api.Commands.TrackApi;
|
||||||
|
using IF.Lastfm.Core.Objects;
|
||||||
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
|
||||||
|
namespace IF.Lastfm.Core.Tests.Integration.Commands
|
||||||
|
{
|
||||||
|
[TestClass]
|
||||||
|
public class TrackUpdateNowPlayingCommandTests : CommandIntegrationTestsBase
|
||||||
|
{
|
||||||
|
private const string ARTIST_NAME = "Crystal Castles";
|
||||||
|
private const string ALBUM_NAME = "Crystal Castles ( II )";
|
||||||
|
private const string TRACK_NAME = "Not in Love";
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public async Task UpdatesNowPlaying()
|
||||||
|
{
|
||||||
|
var trackPlayed = DateTime.UtcNow.AddMinutes(-1);
|
||||||
|
var testScrobble = new Scrobble(ARTIST_NAME, ALBUM_NAME, TRACK_NAME)
|
||||||
|
{
|
||||||
|
TimePlayed = trackPlayed,
|
||||||
|
Duration = new TimeSpan(0, 3, 49),
|
||||||
|
AlbumArtist = ARTIST_NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
var trackApi = new TrackApi(Auth);
|
||||||
|
var response = await trackApi.UpdateNowPlayingAsync(testScrobble);
|
||||||
|
|
||||||
|
Assert.IsTrue(response.Success);
|
||||||
|
|
||||||
|
var userApi = new UserApi(Auth);
|
||||||
|
var tracks = await userApi.GetRecentScrobbles(Auth.UserSession.Username, null, 1, 1);
|
||||||
|
|
||||||
|
var expectedTrack = new LastTrack
|
||||||
|
{
|
||||||
|
Name = TRACK_NAME,
|
||||||
|
ArtistName = ARTIST_NAME,
|
||||||
|
AlbumName = ALBUM_NAME,
|
||||||
|
Mbid = "1b9ee1d8-c5a7-44d9-813e-85beb0d59f1b",
|
||||||
|
ArtistMbid = "b1570544-93ab-4b2b-8398-131735394202",
|
||||||
|
Url = new Uri("http://www.last.fm/music/Crystal+Castles/_/Not+in+Love"),
|
||||||
|
Images = new LastImageSet("http://userserve-ak.last.fm/serve/34s/61473043.png",
|
||||||
|
"http://userserve-ak.last.fm/serve/64s/61473043.png",
|
||||||
|
"http://userserve-ak.last.fm/serve/126/61473043.png",
|
||||||
|
"http://userserve-ak.last.fm/serve/300x300/61473043.png"),
|
||||||
|
IsNowPlaying = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedJson = expectedTrack.TestSerialise();
|
||||||
|
var actualJson = tracks.Content.FirstOrDefault().TestSerialise();
|
||||||
|
|
||||||
|
Assert.AreEqual(expectedJson, actualJson, expectedJson.DifferencesTo(actualJson));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Commands\CommandIntegrationTestsBase.cs" />
|
<Compile Include="Commands\CommandIntegrationTestsBase.cs" />
|
||||||
<Compile Include="Commands\TrackScrobbleCommandTests.cs" />
|
<Compile Include="Commands\TrackScrobbleCommandTests.cs" />
|
||||||
|
<Compile Include="Commands\TrackUpdateNowPlayingCommandTests.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -18,7 +18,7 @@ public class UserGetRecentTracksCommandTests : CommandTestsBase
|
|||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Initialise()
|
public void Initialise()
|
||||||
{
|
{
|
||||||
_command = new UserGetRecentTracksCommand(MAuth.Object, "rj", DateTime.MinValue)
|
_command = new UserGetRecentTracksCommand(MAuth.Object, "rj")
|
||||||
{
|
{
|
||||||
Count = 1
|
Count = 1
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@ -7,13 +8,13 @@ namespace IF.Lastfm.Core.Tests
|
|||||||
{
|
{
|
||||||
public static class TestHelper
|
public static class TestHelper
|
||||||
{
|
{
|
||||||
private static JsonSerializerSettings _testSerialiserSettings;
|
private static readonly JsonSerializerSettings _testSerialiserSettings;
|
||||||
|
|
||||||
static TestHelper()
|
static TestHelper()
|
||||||
{
|
{
|
||||||
_testSerialiserSettings = new JsonSerializerSettings
|
_testSerialiserSettings = new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
DateFormatString = "yyyy-MM-dd HH:mm:ss.SSS zzz",
|
DateFormatString = "yyyy-MM-dd HH:mm:ss.fff",
|
||||||
NullValueHandling = NullValueHandling.Ignore
|
NullValueHandling = NullValueHandling.Ignore
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -73,5 +74,14 @@ public static IEnumerable<T> WrapEnumerable<T>(this T t)
|
|||||||
{
|
{
|
||||||
return new[] {t};
|
return new[] {t};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DateTime RoundToNearestSecond(this DateTime dt)
|
||||||
|
{
|
||||||
|
var ms = dt.Millisecond;
|
||||||
|
|
||||||
|
return ms < 500
|
||||||
|
? dt.AddMilliseconds(-ms)
|
||||||
|
: dt.AddMilliseconds(1000 - ms);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
using System;
|
#region
|
||||||
using System.Collections.Generic;
|
|
||||||
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using IF.Lastfm.Core.Api.Commands.ArtistApi;
|
using IF.Lastfm.Core.Api.Commands.ArtistApi;
|
||||||
using IF.Lastfm.Core.Api.Helpers;
|
using IF.Lastfm.Core.Api.Helpers;
|
||||||
using IF.Lastfm.Core.Objects;
|
using IF.Lastfm.Core.Objects;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
namespace IF.Lastfm.Core.Api
|
namespace IF.Lastfm.Core.Api
|
||||||
{
|
{
|
||||||
public class ArtistApi : IArtistApi
|
public class ArtistApi : IArtistApi
|
||||||
{
|
{
|
||||||
public ILastAuth Auth { get; private set; }
|
|
||||||
|
|
||||||
public ArtistApi(ILastAuth auth)
|
public ArtistApi(ILastAuth auth)
|
||||||
{
|
{
|
||||||
Auth = auth;
|
Auth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ILastAuth Auth { get; private set; }
|
||||||
|
|
||||||
public async Task<LastResponse<LastArtist>> GetArtistInfoAsync(string artist,
|
public async Task<LastResponse<LastArtist>> GetArtistInfoAsync(string artist,
|
||||||
string bioLang = LastFm.DefaultLanguageCode,
|
string bioLang = LastFm.DefaultLanguageCode,
|
||||||
bool autocorrect = false)
|
bool autocorrect = false)
|
||||||
@ -70,7 +73,8 @@ public async Task<PageResponse<LastTrack>> GetTopTracksForArtistAsync(string art
|
|||||||
return await command.ExecuteAsync();
|
return await command.ExecuteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PageResponse<LastArtist>> GetSimilarArtistsAsync(string artistname, bool autocorrect = false, int limit = 100)
|
public async Task<PageResponse<LastArtist>> GetSimilarArtistsAsync(string artistname, bool autocorrect = false,
|
||||||
|
int limit = 100)
|
||||||
{
|
{
|
||||||
var command = new GetSimilarArtistsCommand(Auth, artistname)
|
var command = new GetSimilarArtistsCommand(Auth, artistname)
|
||||||
{
|
{
|
||||||
@ -94,7 +98,8 @@ public async Task<PageResponse<LastTag>> GetTopTagsForArtistAsync(string artist,
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PageResponse<LastShout>> GetShoutsForArtistAsync(string artist, int page = 0, int count = LastFm.DefaultPageLength, bool autocorrect = false)
|
public async Task<PageResponse<LastShout>> GetShoutsForArtistAsync(string artist, int page = 0,
|
||||||
|
int count = LastFm.DefaultPageLength, bool autocorrect = false)
|
||||||
{
|
{
|
||||||
var command = new GetArtistShoutsCommand(Auth, artist)
|
var command = new GetArtistShoutsCommand(Auth, artist)
|
||||||
{
|
{
|
||||||
@ -112,7 +117,8 @@ public async Task<LastResponse> AddShoutAsync(string artistname, string messaage
|
|||||||
return await command.ExecuteAsync();
|
return await command.ExecuteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PageResponse<LastArtist>> SearchForArtistAsync(string artistname, int page = 1, int itemsPerPage = LastFm.DefaultPageLength)
|
public async Task<PageResponse<LastArtist>> SearchForArtistAsync(string artistname, int page = 1,
|
||||||
|
int itemsPerPage = LastFm.DefaultPageLength)
|
||||||
{
|
{
|
||||||
var command = new SearchArtistsCommand(Auth, artistname)
|
var command = new SearchArtistsCommand(Auth, artistname)
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace IF.Lastfm.Core.Api.Commands
|
namespace IF.Lastfm.Core.Api.Commands
|
||||||
{
|
{
|
||||||
internal abstract class PostAsyncCommandBase<T> : LastAsyncCommandBase<T> where T : LastResponse, new()
|
public abstract class PostAsyncCommandBase<T> : LastAsyncCommandBase<T> where T : LastResponse, new()
|
||||||
{
|
{
|
||||||
protected PostAsyncCommandBase(ILastAuth auth)
|
protected PostAsyncCommandBase(ILastAuth auth)
|
||||||
{
|
{
|
||||||
|
@ -16,11 +16,11 @@ internal class TrackScrobbleCommand : PostAsyncCommandBase<LastResponse>
|
|||||||
|
|
||||||
public string AlbumArtist { get; set; }
|
public string AlbumArtist { get; set; }
|
||||||
|
|
||||||
public DateTime TimePlayed { get; set; }
|
public DateTime? TimePlayed { get; set; }
|
||||||
|
|
||||||
public bool ChosenByUser { get; set; }
|
public bool ChosenByUser { get; set; }
|
||||||
|
|
||||||
public TrackScrobbleCommand(ILastAuth auth, string artist, string album, string track, string albumArtist, DateTime timeplayed)
|
public TrackScrobbleCommand(ILastAuth auth, string artist, string album, string track, string albumArtist, DateTime? timeplayed)
|
||||||
: base(auth)
|
: base(auth)
|
||||||
{
|
{
|
||||||
Method = "track.scrobble";
|
Method = "track.scrobble";
|
||||||
@ -45,7 +45,11 @@ public override void SetParameters()
|
|||||||
Parameters.Add("track", Track);
|
Parameters.Add("track", Track);
|
||||||
Parameters.Add("albumArtist", AlbumArtist);
|
Parameters.Add("albumArtist", AlbumArtist);
|
||||||
Parameters.Add("chosenByUser", Convert.ToInt32(ChosenByUser).ToString());
|
Parameters.Add("chosenByUser", Convert.ToInt32(ChosenByUser).ToString());
|
||||||
Parameters.Add("timestamp", TimePlayed.ToUnixTimestamp().ToString());
|
|
||||||
|
if (TimePlayed.HasValue)
|
||||||
|
{
|
||||||
|
Parameters.Add("timestamp", TimePlayed.Value.ToUnixTimestamp().ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async override Task<LastResponse> HandleResponse(HttpResponseMessage response)
|
public async override Task<LastResponse> HandleResponse(HttpResponseMessage response)
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
using IF.Lastfm.Core.Api.Enums;
|
||||||
|
using IF.Lastfm.Core.Api.Helpers;
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IF.Lastfm.Core.Api.Commands.TrackApi
|
||||||
|
{
|
||||||
|
internal class TrackUpdateNowPlayingCommand : PostAsyncCommandBase<LastResponse>
|
||||||
|
{
|
||||||
|
public string Artist { get; set; }
|
||||||
|
|
||||||
|
public string Album { get; set; }
|
||||||
|
|
||||||
|
public string Track { get; set; }
|
||||||
|
|
||||||
|
public string AlbumArtist { get; set; }
|
||||||
|
|
||||||
|
public bool ChosenByUser { get; set; }
|
||||||
|
|
||||||
|
public TimeSpan? Duration { get; set; }
|
||||||
|
|
||||||
|
public TrackUpdateNowPlayingCommand(ILastAuth auth, string artist, string album, string track)
|
||||||
|
: base(auth)
|
||||||
|
{
|
||||||
|
Method = "track.updateNowPlaying";
|
||||||
|
|
||||||
|
Artist = artist;
|
||||||
|
Album = album;
|
||||||
|
Track = track;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrackUpdateNowPlayingCommand(ILastAuth auth, Scrobble scrobble)
|
||||||
|
: this(auth, scrobble.Artist, scrobble.Album, scrobble.Track)
|
||||||
|
{
|
||||||
|
ChosenByUser = scrobble.ChosenByUser;
|
||||||
|
Duration = scrobble.Duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetParameters()
|
||||||
|
{
|
||||||
|
Parameters.Add("artist", Artist);
|
||||||
|
Parameters.Add("album", Album);
|
||||||
|
Parameters.Add("track", Track);
|
||||||
|
Parameters.Add("albumArtist", AlbumArtist);
|
||||||
|
Parameters.Add("chosenByUser", Convert.ToInt32(ChosenByUser).ToString());
|
||||||
|
|
||||||
|
if (Duration.HasValue)
|
||||||
|
{
|
||||||
|
Parameters.Add("duration", Duration.Value.TotalSeconds.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async override Task<LastResponse> HandleResponse(HttpResponseMessage response)
|
||||||
|
{
|
||||||
|
var json = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
LastFmApiError error;
|
||||||
|
if (LastFm.IsResponseValid(json, out error) && response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return LastResponse.CreateSuccessResponse();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return LastResponse.CreateErrorResponse<LastResponse>(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,20 +15,23 @@ internal class UserGetRecentTracksCommand : GetAsyncCommandBase<PageResponse<Las
|
|||||||
{
|
{
|
||||||
public string Username { get; private set; }
|
public string Username { get; private set; }
|
||||||
|
|
||||||
public DateTime From { get; private set; }
|
public DateTime? From { get; set; }
|
||||||
|
|
||||||
public UserGetRecentTracksCommand(ILastAuth auth, string username, DateTime from) : base(auth)
|
public UserGetRecentTracksCommand(ILastAuth auth, string username) : base(auth)
|
||||||
{
|
{
|
||||||
Method = "user.getRecentTracks";
|
Method = "user.getRecentTracks";
|
||||||
|
|
||||||
Username = username;
|
Username = username;
|
||||||
From = from;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetParameters()
|
public override void SetParameters()
|
||||||
{
|
{
|
||||||
Parameters.Add("user", Username);
|
Parameters.Add("user", Username);
|
||||||
Parameters.Add("from", From.ToUnixTimestamp().ToString());
|
|
||||||
|
if (From.HasValue)
|
||||||
|
{
|
||||||
|
Parameters.Add("from", From.Value.ToUnixTimestamp().ToString());
|
||||||
|
}
|
||||||
|
|
||||||
AddPagingParameters();
|
AddPagingParameters();
|
||||||
DisableCaching();
|
DisableCaching();
|
||||||
|
@ -29,7 +29,7 @@ public static string GetApiName(this Enum enumValue)
|
|||||||
|
|
||||||
public static int ToUnixTimestamp(this DateTime dt)
|
public static int ToUnixTimestamp(this DateTime dt)
|
||||||
{
|
{
|
||||||
var d = (dt - new DateTime(1970, 1, 1).ToUniversalTime()).TotalSeconds;
|
var d = (dt - new DateTime(1970, 1, 1)).TotalSeconds;
|
||||||
|
|
||||||
return Convert.ToInt32(d);
|
return Convert.ToInt32(d);
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,16 @@ public interface IUserApi
|
|||||||
{
|
{
|
||||||
ILastAuth Auth { get; }
|
ILastAuth Auth { get; }
|
||||||
|
|
||||||
|
Task<PageResponse<LastArtist>> GetRecommendedArtistsAsync(
|
||||||
|
int page = 1,
|
||||||
|
int itemsPerPage = LastFm.DefaultPageLength);
|
||||||
|
|
||||||
Task<PageResponse<LastAlbum>> GetTopAlbums(string username,
|
Task<PageResponse<LastAlbum>> GetTopAlbums(string username,
|
||||||
LastStatsTimeSpan span,
|
LastStatsTimeSpan span,
|
||||||
int startIndex = 0,
|
int startIndex = 0,
|
||||||
int endIndex = LastFm.DefaultPageLength);
|
int endIndex = LastFm.DefaultPageLength);
|
||||||
|
|
||||||
Task<PageResponse<LastTrack>> GetRecentScrobbles(string username,
|
Task<PageResponse<LastTrack>> GetRecentScrobbles(string username, DateTime? since = null, int pagenumber = 0, int count = LastFm.DefaultPageLength);
|
||||||
DateTime since,
|
|
||||||
int startIndex = 0,
|
|
||||||
int endIndex = LastFm.DefaultPageLength);
|
|
||||||
|
|
||||||
Task<PageResponse<LastStation>> GetRecentStations(string username,
|
Task<PageResponse<LastStation>> GetRecentStations(string username,
|
||||||
int pagenumber,
|
int pagenumber,
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Arcnet.MyConvert;
|
|
||||||
using IF.Lastfm.Core.Api.Commands.AuthApi;
|
using IF.Lastfm.Core.Api.Commands.AuthApi;
|
||||||
using IF.Lastfm.Core.Api.Helpers;
|
using IF.Lastfm.Core.Api.Helpers;
|
||||||
using IF.Lastfm.Core.Objects;
|
using IF.Lastfm.Core.Objects;
|
||||||
@ -74,7 +73,7 @@ public string GenerateMethodSignature(string method, Dictionary<string, string>
|
|||||||
|
|
||||||
builder.Append(_apiSecret);
|
builder.Append(_apiSecret);
|
||||||
|
|
||||||
var md5 = builder.ToString().ToMd5();
|
var md5 = MD5.GetHashString(builder.ToString());
|
||||||
|
|
||||||
return md5;
|
return md5;
|
||||||
|
|
||||||
|
@ -7,22 +7,26 @@ public class Scrobble
|
|||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
public string Artist { get; private set; }
|
public string Artist { get; private set; }
|
||||||
public string AlbumArtist { get; private set; }
|
|
||||||
|
public string AlbumArtist { get; set; }
|
||||||
|
|
||||||
public string Album { get; private set; }
|
public string Album { get; private set; }
|
||||||
|
|
||||||
public string Track { get; private set; }
|
public string Track { get; private set; }
|
||||||
public DateTime TimePlayed { get; private set; }
|
|
||||||
public bool ChosenByUser { get; private set; }
|
public DateTime? TimePlayed { get; set; }
|
||||||
|
|
||||||
|
public bool ChosenByUser { get; set; }
|
||||||
|
|
||||||
|
public TimeSpan? Duration { get; set; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public Scrobble(string artist, string album, string track, DateTime timeplayed, string albumartist = "", bool chosenByUser = true)
|
public Scrobble(string artist, string album, string track)
|
||||||
{
|
{
|
||||||
Artist = artist;
|
Artist = artist;
|
||||||
Album = album;
|
Album = album;
|
||||||
Track = track;
|
Track = track;
|
||||||
TimePlayed = timeplayed;
|
|
||||||
AlbumArtist = string.IsNullOrWhiteSpace(albumartist) ? artist : albumartist;
|
|
||||||
ChosenByUser = chosenByUser;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,7 @@
|
|||||||
using IF.Lastfm.Core.Api.Commands.TrackApi;
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using IF.Lastfm.Core.Api.Commands.TrackApi;
|
||||||
|
using IF.Lastfm.Core.Api.Enums;
|
||||||
using IF.Lastfm.Core.Api.Helpers;
|
using IF.Lastfm.Core.Api.Helpers;
|
||||||
using IF.Lastfm.Core.Objects;
|
using IF.Lastfm.Core.Objects;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -20,6 +23,12 @@ public Task<LastResponse> ScrobbleAsync(Scrobble scrobble)
|
|||||||
return command.ExecuteAsync();
|
return command.ExecuteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<LastResponse> UpdateNowPlayingAsync(Scrobble scrobble)
|
||||||
|
{
|
||||||
|
var command = new TrackUpdateNowPlayingCommand(Auth, scrobble);
|
||||||
|
return command.ExecuteAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<PageResponse<LastShout>> GetShoutsForTrackAsync(string trackname, string artistname, bool autocorrect = false, int page = 0, int count = LastFm.DefaultPageLength)
|
public async Task<PageResponse<LastShout>> GetShoutsForTrackAsync(string trackname, string artistname, bool autocorrect = false, int page = 0, int count = LastFm.DefaultPageLength)
|
||||||
{
|
{
|
||||||
var command = new GetTrackShoutsCommand(Auth, trackname, artistname)
|
var command = new GetTrackShoutsCommand(Auth, trackname, artistname)
|
||||||
|
@ -16,6 +16,16 @@ public UserApi(ILastAuth auth)
|
|||||||
Auth = auth;
|
Auth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<PageResponse<LastArtist>> GetRecommendedArtistsAsync(int page = 1, int itemsPerPage = LastFm.DefaultPageLength)
|
||||||
|
{
|
||||||
|
var command = new GetRecommendedArtistsCommand(Auth)
|
||||||
|
{
|
||||||
|
Page = page,
|
||||||
|
Count = itemsPerPage
|
||||||
|
};
|
||||||
|
return await command.ExecuteAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<PageResponse<LastAlbum>> GetTopAlbums(string username, LastStatsTimeSpan span, int pagenumber = 0, int count = LastFm.DefaultPageLength)
|
public async Task<PageResponse<LastAlbum>> GetTopAlbums(string username, LastStatsTimeSpan span, int pagenumber = 0, int count = LastFm.DefaultPageLength)
|
||||||
{
|
{
|
||||||
var command = new GetTopAlbumsCommand(Auth, username, span)
|
var command = new GetTopAlbumsCommand(Auth, username, span)
|
||||||
@ -27,12 +37,21 @@ public async Task<PageResponse<LastAlbum>> GetTopAlbums(string username, LastSta
|
|||||||
return await command.ExecuteAsync();
|
return await command.ExecuteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PageResponse<LastTrack>> GetRecentScrobbles(string username, DateTime since, int pagenumber = 0, int count = LastFm.DefaultPageLength)
|
/// <summary>
|
||||||
|
/// Gets a list of recent scrobbled tracks for this user in reverse date order.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="username">Username to get scrobbles for.</param>
|
||||||
|
/// <param name="since">Lower threshold for scrobbles. Will not return scrobbles from before this time.</param>
|
||||||
|
/// <param name="pagenumber">Page numbering starts from 1. If set to 0, will not include the "now playing" track</param>
|
||||||
|
/// <param name="count">Amount of scrobbles to return for this page.</param>
|
||||||
|
/// <returns>Enumerable of LastTrack</returns>
|
||||||
|
public async Task<PageResponse<LastTrack>> GetRecentScrobbles(string username, DateTime? since = null, int pagenumber = 0, int count = LastFm.DefaultPageLength)
|
||||||
{
|
{
|
||||||
var command = new UserGetRecentTracksCommand(Auth, username, since)
|
var command = new UserGetRecentTracksCommand(Auth, username)
|
||||||
{
|
{
|
||||||
Page = pagenumber,
|
Page = pagenumber,
|
||||||
Count = count
|
Count = count,
|
||||||
|
From = since
|
||||||
};
|
};
|
||||||
|
|
||||||
return await command.ExecuteAsync();
|
return await command.ExecuteAsync();
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
<Compile Include="Api\Commands\AlbumApi\GetAlbumTopTagsCommand.cs" />
|
<Compile Include="Api\Commands\AlbumApi\GetAlbumTopTagsCommand.cs" />
|
||||||
<Compile Include="Api\Commands\LibraryApi\LibraryGetTracksCommand.cs" />
|
<Compile Include="Api\Commands\LibraryApi\LibraryGetTracksCommand.cs" />
|
||||||
<Compile Include="Api\Commands\TrackApi\TrackScrobbleCommand.cs" />
|
<Compile Include="Api\Commands\TrackApi\TrackScrobbleCommand.cs" />
|
||||||
|
<Compile Include="Api\Commands\TrackApi\TrackUpdateNowPlayingCommand.cs" />
|
||||||
<Compile Include="Api\Commands\UnauthenticatedPostAsyncCommandBase.cs" />
|
<Compile Include="Api\Commands\UnauthenticatedPostAsyncCommandBase.cs" />
|
||||||
<Compile Include="Api\Commands\UserApi\GetRecommendedArtistsCommand.cs" />
|
<Compile Include="Api\Commands\UserApi\GetRecommendedArtistsCommand.cs" />
|
||||||
<Compile Include="Api\ILibraryApi.cs" />
|
<Compile Include="Api\ILibraryApi.cs" />
|
||||||
@ -98,6 +99,7 @@
|
|||||||
<Compile Include="Api\UserApi.cs" />
|
<Compile Include="Api\UserApi.cs" />
|
||||||
<Compile Include="Json\LastFmBooleanConverter.cs" />
|
<Compile Include="Json\LastFmBooleanConverter.cs" />
|
||||||
<Compile Include="LastFm.cs" />
|
<Compile Include="LastFm.cs" />
|
||||||
|
<Compile Include="MD5.cs" />
|
||||||
<Compile Include="Objects\LastAlbum.cs" />
|
<Compile Include="Objects\LastAlbum.cs" />
|
||||||
<Compile Include="Objects\LastArtist.cs" />
|
<Compile Include="Objects\LastArtist.cs" />
|
||||||
<Compile Include="Objects\BuyLink.cs" />
|
<Compile Include="Objects\BuyLink.cs" />
|
||||||
@ -114,9 +116,6 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="MyConvert">
|
|
||||||
<HintPath>..\..\packages\MyConvert.1.0.1.6\lib\MyConvert.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Newtonsoft.Json">
|
<Reference Include="Newtonsoft.Json">
|
||||||
<HintPath>..\..\packages\Newtonsoft.Json.6.0.5\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\..\packages\Newtonsoft.Json.6.0.5\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
295
src/IF.Lastfm.Core/MD5.cs
Normal file
295
src/IF.Lastfm.Core/MD5.cs
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
//// **************************************************************
|
||||||
|
//// * Raw implementation of the MD5 hash algorithm
|
||||||
|
//// * from RFC 1321.
|
||||||
|
//// *
|
||||||
|
//// * Written By: Reid Borsuk and Jenny Zheng
|
||||||
|
//// * Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
//// **************************************************************
|
||||||
|
|
||||||
|
#region
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace IF.Lastfm.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Simple struct for the (a,b,c,d) which is used to compute the mesage digest.
|
||||||
|
/// </summary>
|
||||||
|
internal struct ABCDStruct
|
||||||
|
{
|
||||||
|
public uint A;
|
||||||
|
public uint B;
|
||||||
|
public uint C;
|
||||||
|
public uint D;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raw implementation of the MD5 hash algorithm rom RFC 1321.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MD5
|
||||||
|
{
|
||||||
|
//// Prevent CSC from adding a default public constructor
|
||||||
|
private MD5()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] GetHash(string input, Encoding encoding)
|
||||||
|
{
|
||||||
|
if (null == input)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("input", "Unable to calculate hash over null input data");
|
||||||
|
}
|
||||||
|
if (null == encoding)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("encoding",
|
||||||
|
"Unable to calculate hash over a string without a default encoding. Consider using the GetHash(string) overload to use UTF8 Encoding");
|
||||||
|
}
|
||||||
|
var target = encoding.GetBytes(input);
|
||||||
|
return GetHash(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] GetHash(string input)
|
||||||
|
{
|
||||||
|
return GetHash(input, new UTF8Encoding());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetHashString(byte[] input)
|
||||||
|
{
|
||||||
|
if (null == input)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("input", "Unable to calculate hash over null input data");
|
||||||
|
}
|
||||||
|
var retval = BitConverter.ToString(GetHash(input));
|
||||||
|
retval = retval.Replace("-", string.Empty);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetHashString(string input, Encoding encoding)
|
||||||
|
{
|
||||||
|
if (null == input)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("input", "Unable to calculate hash over null input data");
|
||||||
|
}
|
||||||
|
if (null == encoding)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("encoding",
|
||||||
|
"Unable to calculate hash over a string without a default encoding. Consider using the GetHashString(string) overload to use UTF8 Encoding");
|
||||||
|
}
|
||||||
|
var target = encoding.GetBytes(input);
|
||||||
|
return GetHashString(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetHashString(string input)
|
||||||
|
{
|
||||||
|
return GetHashString(input, new UTF8Encoding());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] GetHash(byte[] input)
|
||||||
|
{
|
||||||
|
if (null == input)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("input", "Unable to calculate hash over null input data");
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Intitial values defined in RFC 1321
|
||||||
|
var abcd = new ABCDStruct();
|
||||||
|
abcd.A = 0x67452301;
|
||||||
|
abcd.B = 0xefcdab89;
|
||||||
|
abcd.C = 0x98badcfe;
|
||||||
|
abcd.D = 0x10325476;
|
||||||
|
|
||||||
|
//// We pass in the input array by block, the final block of data must be handled specialy for padding & length embeding
|
||||||
|
var startIndex = 0;
|
||||||
|
while (startIndex <= input.Length - 64)
|
||||||
|
{
|
||||||
|
GetHashBlock(input, ref abcd, startIndex);
|
||||||
|
startIndex += 64;
|
||||||
|
}
|
||||||
|
//// The final data block.
|
||||||
|
return GetHashFinalBlock(input, startIndex, input.Length - startIndex, abcd, (Int64) input.Length*8);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static byte[] GetHashFinalBlock(byte[] input, int ibStart, int cbSize, ABCDStruct ABCD, Int64 len)
|
||||||
|
{
|
||||||
|
var working = new byte[64];
|
||||||
|
var length = BitConverter.GetBytes(len);
|
||||||
|
|
||||||
|
//// Padding is a single bit 1, followed by the number of 0s required to make size congruent to 448 modulo 512. Step 1 of RFC 1321
|
||||||
|
//// The CLR ensures that our buffer is 0-assigned, we don't need to explicitly set it. This is why it ends up being quicker to just
|
||||||
|
//// use a temporary array rather then doing in-place assignment (5% for small inputs)
|
||||||
|
Array.Copy(input, ibStart, working, 0, cbSize);
|
||||||
|
working[cbSize] = 0x80;
|
||||||
|
|
||||||
|
//// We have enough room to store the length in this chunk
|
||||||
|
if (cbSize < 56)
|
||||||
|
{
|
||||||
|
Array.Copy(length, 0, working, 56, 8);
|
||||||
|
GetHashBlock(working, ref ABCD, 0);
|
||||||
|
}
|
||||||
|
else //// We need an aditional chunk to store the length
|
||||||
|
{
|
||||||
|
GetHashBlock(working, ref ABCD, 0);
|
||||||
|
//// Create an entirely new chunk due to the 0-assigned trick mentioned above, to avoid an extra function call clearing the array
|
||||||
|
working = new byte[64];
|
||||||
|
Array.Copy(length, 0, working, 56, 8);
|
||||||
|
GetHashBlock(working, ref ABCD, 0);
|
||||||
|
}
|
||||||
|
var output = new byte[16];
|
||||||
|
Array.Copy(BitConverter.GetBytes(ABCD.A), 0, output, 0, 4);
|
||||||
|
Array.Copy(BitConverter.GetBytes(ABCD.B), 0, output, 4, 4);
|
||||||
|
Array.Copy(BitConverter.GetBytes(ABCD.C), 0, output, 8, 4);
|
||||||
|
Array.Copy(BitConverter.GetBytes(ABCD.D), 0, output, 12, 4);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Performs a single block transform of MD5 for a given set of ABCD inputs
|
||||||
|
/* If implementing your own hashing framework, be sure to set the initial ABCD correctly according to RFC 1321:
|
||||||
|
// A = 0x67452301;
|
||||||
|
// B = 0xefcdab89;
|
||||||
|
// C = 0x98badcfe;
|
||||||
|
// D = 0x10325476;
|
||||||
|
*/
|
||||||
|
|
||||||
|
internal static void GetHashBlock(byte[] input, ref ABCDStruct ABCDValue, int ibStart)
|
||||||
|
{
|
||||||
|
var temp = Converter(input, ibStart);
|
||||||
|
var a = ABCDValue.A;
|
||||||
|
var b = ABCDValue.B;
|
||||||
|
var c = ABCDValue.C;
|
||||||
|
var d = ABCDValue.D;
|
||||||
|
|
||||||
|
a = r1(a, b, c, d, temp[0], 7, 0xd76aa478);
|
||||||
|
d = r1(d, a, b, c, temp[1], 12, 0xe8c7b756);
|
||||||
|
c = r1(c, d, a, b, temp[2], 17, 0x242070db);
|
||||||
|
b = r1(b, c, d, a, temp[3], 22, 0xc1bdceee);
|
||||||
|
a = r1(a, b, c, d, temp[4], 7, 0xf57c0faf);
|
||||||
|
d = r1(d, a, b, c, temp[5], 12, 0x4787c62a);
|
||||||
|
c = r1(c, d, a, b, temp[6], 17, 0xa8304613);
|
||||||
|
b = r1(b, c, d, a, temp[7], 22, 0xfd469501);
|
||||||
|
a = r1(a, b, c, d, temp[8], 7, 0x698098d8);
|
||||||
|
d = r1(d, a, b, c, temp[9], 12, 0x8b44f7af);
|
||||||
|
c = r1(c, d, a, b, temp[10], 17, 0xffff5bb1);
|
||||||
|
b = r1(b, c, d, a, temp[11], 22, 0x895cd7be);
|
||||||
|
a = r1(a, b, c, d, temp[12], 7, 0x6b901122);
|
||||||
|
d = r1(d, a, b, c, temp[13], 12, 0xfd987193);
|
||||||
|
c = r1(c, d, a, b, temp[14], 17, 0xa679438e);
|
||||||
|
b = r1(b, c, d, a, temp[15], 22, 0x49b40821);
|
||||||
|
|
||||||
|
a = r2(a, b, c, d, temp[1], 5, 0xf61e2562);
|
||||||
|
d = r2(d, a, b, c, temp[6], 9, 0xc040b340);
|
||||||
|
c = r2(c, d, a, b, temp[11], 14, 0x265e5a51);
|
||||||
|
b = r2(b, c, d, a, temp[0], 20, 0xe9b6c7aa);
|
||||||
|
a = r2(a, b, c, d, temp[5], 5, 0xd62f105d);
|
||||||
|
d = r2(d, a, b, c, temp[10], 9, 0x02441453);
|
||||||
|
c = r2(c, d, a, b, temp[15], 14, 0xd8a1e681);
|
||||||
|
b = r2(b, c, d, a, temp[4], 20, 0xe7d3fbc8);
|
||||||
|
a = r2(a, b, c, d, temp[9], 5, 0x21e1cde6);
|
||||||
|
d = r2(d, a, b, c, temp[14], 9, 0xc33707d6);
|
||||||
|
c = r2(c, d, a, b, temp[3], 14, 0xf4d50d87);
|
||||||
|
b = r2(b, c, d, a, temp[8], 20, 0x455a14ed);
|
||||||
|
a = r2(a, b, c, d, temp[13], 5, 0xa9e3e905);
|
||||||
|
d = r2(d, a, b, c, temp[2], 9, 0xfcefa3f8);
|
||||||
|
c = r2(c, d, a, b, temp[7], 14, 0x676f02d9);
|
||||||
|
b = r2(b, c, d, a, temp[12], 20, 0x8d2a4c8a);
|
||||||
|
|
||||||
|
a = r3(a, b, c, d, temp[5], 4, 0xfffa3942);
|
||||||
|
d = r3(d, a, b, c, temp[8], 11, 0x8771f681);
|
||||||
|
c = r3(c, d, a, b, temp[11], 16, 0x6d9d6122);
|
||||||
|
b = r3(b, c, d, a, temp[14], 23, 0xfde5380c);
|
||||||
|
a = r3(a, b, c, d, temp[1], 4, 0xa4beea44);
|
||||||
|
d = r3(d, a, b, c, temp[4], 11, 0x4bdecfa9);
|
||||||
|
c = r3(c, d, a, b, temp[7], 16, 0xf6bb4b60);
|
||||||
|
b = r3(b, c, d, a, temp[10], 23, 0xbebfbc70);
|
||||||
|
a = r3(a, b, c, d, temp[13], 4, 0x289b7ec6);
|
||||||
|
d = r3(d, a, b, c, temp[0], 11, 0xeaa127fa);
|
||||||
|
c = r3(c, d, a, b, temp[3], 16, 0xd4ef3085);
|
||||||
|
b = r3(b, c, d, a, temp[6], 23, 0x04881d05);
|
||||||
|
a = r3(a, b, c, d, temp[9], 4, 0xd9d4d039);
|
||||||
|
d = r3(d, a, b, c, temp[12], 11, 0xe6db99e5);
|
||||||
|
c = r3(c, d, a, b, temp[15], 16, 0x1fa27cf8);
|
||||||
|
b = r3(b, c, d, a, temp[2], 23, 0xc4ac5665);
|
||||||
|
|
||||||
|
a = r4(a, b, c, d, temp[0], 6, 0xf4292244);
|
||||||
|
d = r4(d, a, b, c, temp[7], 10, 0x432aff97);
|
||||||
|
c = r4(c, d, a, b, temp[14], 15, 0xab9423a7);
|
||||||
|
b = r4(b, c, d, a, temp[5], 21, 0xfc93a039);
|
||||||
|
a = r4(a, b, c, d, temp[12], 6, 0x655b59c3);
|
||||||
|
d = r4(d, a, b, c, temp[3], 10, 0x8f0ccc92);
|
||||||
|
c = r4(c, d, a, b, temp[10], 15, 0xffeff47d);
|
||||||
|
b = r4(b, c, d, a, temp[1], 21, 0x85845dd1);
|
||||||
|
a = r4(a, b, c, d, temp[8], 6, 0x6fa87e4f);
|
||||||
|
d = r4(d, a, b, c, temp[15], 10, 0xfe2ce6e0);
|
||||||
|
c = r4(c, d, a, b, temp[6], 15, 0xa3014314);
|
||||||
|
b = r4(b, c, d, a, temp[13], 21, 0x4e0811a1);
|
||||||
|
a = r4(a, b, c, d, temp[4], 6, 0xf7537e82);
|
||||||
|
d = r4(d, a, b, c, temp[11], 10, 0xbd3af235);
|
||||||
|
c = r4(c, d, a, b, temp[2], 15, 0x2ad7d2bb);
|
||||||
|
b = r4(b, c, d, a, temp[9], 21, 0xeb86d391);
|
||||||
|
|
||||||
|
ABCDValue.A = unchecked(a + ABCDValue.A);
|
||||||
|
ABCDValue.B = unchecked(b + ABCDValue.B);
|
||||||
|
ABCDValue.C = unchecked(c + ABCDValue.C);
|
||||||
|
ABCDValue.D = unchecked(d + ABCDValue.D);
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Manually unrolling these equations nets us a 20% performance improvement
|
||||||
|
private static uint r1(uint a, uint b, uint c, uint d, uint x, int s, uint t)
|
||||||
|
{
|
||||||
|
//// (b + LSR((a + F(b, c, d) + x + t), s))
|
||||||
|
//// F(x, y, z) ((x & y) | ((x ^ 0xFFFFFFFF) & z))
|
||||||
|
return unchecked(b + LSR((a + ((b & c) | ((b ^ 0xFFFFFFFF) & d)) + x + t), s));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint r2(uint a, uint b, uint c, uint d, uint x, int s, uint t)
|
||||||
|
{
|
||||||
|
//// (b + LSR((a + G(b, c, d) + x + t), s))
|
||||||
|
//// G(x, y, z) ((x & z) | (y & (z ^ 0xFFFFFFFF)))
|
||||||
|
return unchecked(b + LSR((a + ((b & d) | (c & (d ^ 0xFFFFFFFF))) + x + t), s));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint r3(uint a, uint b, uint c, uint d, uint x, int s, uint t)
|
||||||
|
{
|
||||||
|
//// (b + LSR((a + H(b, c, d) + k + i), s))
|
||||||
|
//// H(x, y, z) (x ^ y ^ z)
|
||||||
|
return unchecked(b + LSR((a + (b ^ c ^ d) + x + t), s));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint r4(uint a, uint b, uint c, uint d, uint x, int s, uint t)
|
||||||
|
{
|
||||||
|
//// (b + LSR((a + I(b, c, d) + k + i), s))
|
||||||
|
//// I(x, y, z) (y ^ (x | (z ^ 0xFFFFFFFF)))
|
||||||
|
return unchecked(b + LSR((a + (c ^ (b | (d ^ 0xFFFFFFFF))) + x + t), s));
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Implementation of left rotate
|
||||||
|
//// s is an int instead of a uint becuase the CLR requires the argument passed to >>/<< is of
|
||||||
|
//// type int. Doing the demoting inside this function would add overhead.
|
||||||
|
private static uint LSR(uint i, int s)
|
||||||
|
{
|
||||||
|
return ((i << s) | (i >> (32 - s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//// Convert input array into array of UInts
|
||||||
|
private static uint[] Converter(byte[] input, int ibStart)
|
||||||
|
{
|
||||||
|
if (null == input)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("input", "Unable convert null array to array of uInts");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new uint[16];
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
result[i] = input[ibStart + i*4];
|
||||||
|
result[i] += (uint) input[ibStart + i*4 + 1] << 8;
|
||||||
|
result[i] += (uint) input[ibStart + i*4 + 2] << 16;
|
||||||
|
result[i] += (uint) input[ibStart + i*4 + 3] << 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -61,8 +61,13 @@ internal static LastTrack ParseJToken(JToken token)
|
|||||||
t.Name = token.Value<string>("name");
|
t.Name = token.Value<string>("name");
|
||||||
t.Mbid = token.Value<string>("mbid");
|
t.Mbid = token.Value<string>("mbid");
|
||||||
|
|
||||||
//0 to null
|
//some tracks do not contain the playcount prop, it will throw a FormatException
|
||||||
t.TotalPlayCount = token.Value<int?>("playcount");
|
var playCountStr = token.Value<string>("playcount");
|
||||||
|
int playCount;
|
||||||
|
if (int.TryParse(playCountStr, out playCount))
|
||||||
|
{
|
||||||
|
t.TotalPlayCount = playCount;
|
||||||
|
}
|
||||||
|
|
||||||
t.Url = new Uri(token.Value<string>("url"), UriKind.Absolute);
|
t.Url = new Uri(token.Value<string>("url"), UriKind.Absolute);
|
||||||
|
|
||||||
|
@ -34,13 +34,19 @@ public class LastWiki : ILastfmObject
|
|||||||
|
|
||||||
internal static LastWiki ParseJToken(JToken token)
|
internal static LastWiki ParseJToken(JToken token)
|
||||||
{
|
{
|
||||||
return new LastWiki
|
var wiki = new LastWiki
|
||||||
{
|
{
|
||||||
Published = token.Value<DateTime>("published"),
|
|
||||||
Summary = token.Value<string>("summary").Trim(),
|
Summary = token.Value<string>("summary").Trim(),
|
||||||
Content = token.Value<string>("content").Trim(),
|
Content = token.Value<string>("content").Trim(),
|
||||||
YearFormed = token.Value<int>("yearformed")
|
YearFormed = token.Value<int>("yearformed")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Artist that do not contain an official bio will come with an empty published property.
|
||||||
|
//To avoid a parse exception, check if is null or empty.
|
||||||
|
if (!string.IsNullOrEmpty(token.Value<string>("published")))
|
||||||
|
wiki.Published = token.Value<DateTime>("published");
|
||||||
|
|
||||||
|
return wiki;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,5 @@
|
|||||||
<package id="Microsoft.Bcl" version="1.1.9" targetFramework="portable-net45+win+wpa81+wp80" />
|
<package id="Microsoft.Bcl" version="1.1.9" targetFramework="portable-net45+win+wpa81+wp80" />
|
||||||
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="portable-net45+win+wpa81+wp80" />
|
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="portable-net45+win+wpa81+wp80" />
|
||||||
<package id="Microsoft.Net.Http" version="2.2.28" targetFramework="portable-net45+win+wpa81+wp80" />
|
<package id="Microsoft.Net.Http" version="2.2.28" targetFramework="portable-net45+win+wpa81+wp80" />
|
||||||
<package id="MyConvert" version="1.0.1.6" targetFramework="portable-net45+win+wpa81+wp80" />
|
|
||||||
<package id="Newtonsoft.Json" version="6.0.5" targetFramework="portable-net45+win+wpa81+wp80" />
|
<package id="Newtonsoft.Json" version="6.0.5" targetFramework="portable-net45+win+wpa81+wp80" />
|
||||||
</packages>
|
</packages>
|
Loading…
Reference in New Issue
Block a user