Scrobble method!

Also making all ParseJToken methods internal, but using
[assembly:InternalsVisibleTo("IF.Lastfm.Core.Tests")] to make them
testable
This commit is contained in:
Rikki Tooley 2013-06-14 16:19:26 +01:00
parent e3e42853a0
commit 600814d09f
13 changed files with 129 additions and 10 deletions

View File

@ -93,12 +93,12 @@ public Task<ListResponse<Shout>> GetShoutsForAlbumWithMbidAsync(string mbid, boo
#region album.getTags
public Task<ListResponse<Tag>> GetUserTagsForAlbumAsync(string artist, string album, bool autocorrect = false)
public Task<ListResponse<Tag>> GetUserTagsForAlbumAsync(string artist, string album, string username, bool autocorrect = false)
{
throw new NotImplementedException();
}
public Task<ListResponse<Tag>> GetUserTagsForAlbumWithMbidAsync(string mbid, bool autocorrect = false)
public Task<ListResponse<Tag>> GetUserTagsForAlbumWithMbidAsync(string mbid, string username, bool autocorrect = false)
{
throw new NotImplementedException();
}

View File

@ -20,6 +20,7 @@ public class Auth : IAuth
public bool HasAuthenticated { get { return User != null; } }
public string ApiKey { get; private set; }
public UserSession User { get; private set; }
public string ApiSignature { get; private set; }
public Auth(string apikey, string secret)
{
@ -27,15 +28,14 @@ public Auth(string apikey, string secret)
_apiSecret = secret;
}
public async Task<LastResponse> GetSessionTokenAsync(string username, string password)
public async Task<LastResponse> GetSessionTokenAsync(string username, string password)
{
const string apiMethod = "auth.getMobileSession";
var apisigseed = string.Format(ApiSignatureSeedFormat, ApiKey, ApiAuthMethod, password, username, _apiSecret);
ApiSignature = MD5.GetHashString(apisigseed);
var apisig = MD5.GetHashString(apisigseed);
var postContent = LastFm.CreatePostBody(apiMethod, ApiKey, apisig, new Dictionary<string, string>
var postContent = LastFm.CreatePostBody(apiMethod, ApiKey, ApiSignature, new Dictionary<string, string>
{
{"password", password},
{"username", username}

View File

@ -22,5 +22,15 @@ public static string GetApiName(this Enum en)
return en.ToString();
}
public static int ToInt(this bool b)
{
return b ? 1 : 0;
}
public static double ToUnixTimestamp(this DateTime dt)
{
return (dt - new DateTime(1970, 1, 1).ToUniversalTime()).TotalSeconds;
}
}
}

View File

@ -9,6 +9,7 @@ public interface IAuth
bool HasAuthenticated { get; }
string ApiKey { get; }
UserSession User { get; }
string ApiSignature { get; }
/// <summary>
/// Gets the session token which is used as authentication for any service calls.

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using IF.Lastfm.Core.Api.Helpers;
using xBrainLab.Security.Cryptography;
namespace IF.Lastfm.Core.Api
{
public interface ITrackApi
{
IAuth Auth { get; }
Task<LastResponse> ScrobbleAsync(Scrobble scrobble);
Task<LastResponse> ScrobbleAsync(IEnumerable<Scrobble> scrobble);
}
}

View File

@ -0,0 +1,28 @@
using System;
namespace IF.Lastfm.Core.Api
{
public class Scrobble
{
#region Properties
public string Artist { get; private set; }
public string AlbumArtist { get; private set; }
public string Album { get; private set; }
public string Track { get; private set; }
public DateTime TimePlayed { get; private set; }
public bool ChosenByUser { get; private set; }
#endregion
public Scrobble(string artist, string album, string track, DateTime timeplayed, string albumartist = "", bool chosenByUser = true)
{
Artist = artist;
Album = album;
Track = track;
TimePlayed = timeplayed;
AlbumArtist = string.IsNullOrWhiteSpace(albumartist) ? artist : albumartist;
ChosenByUser = chosenByUser;
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using IF.Lastfm.Core.Api.Enums;
using IF.Lastfm.Core.Api.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace IF.Lastfm.Core.Api
{
public class TrackApi : ITrackApi
{
public IAuth Auth { get; private set; }
public TrackApi(IAuth auth)
{
Auth = auth;
}
public async Task<LastResponse> ScrobbleAsync(Scrobble scrobble)
{
const string apiMethod = "track.scrobble";
var postContent = LastFm.CreatePostBody(apiMethod,
Auth.ApiKey,
Auth.ApiSignature,
new Dictionary<string, string>
{
{"artist", scrobble.Artist},
{"album", scrobble.Album},
{"track", scrobble.Track},
{"albumArtist", scrobble.AlbumArtist},
{"chosenByUser", scrobble.ChosenByUser.ToInt().ToString()},
{"timestamp", scrobble.TimePlayed.ToUnixTimestamp().ToString()}
});
var httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.PostAsync(LastFm.ApiRoot, postContent);
string json = await response.Content.ReadAsStringAsync();
LastFmApiError error;
if (LastFm.IsResponseValid(json, out error) && response.IsSuccessStatusCode)
{
return LastResponse.CreateSuccessResponse();
}
else
{
return LastResponse.CreateErrorResponse(error);
}
}
public Task<LastResponse> ScrobbleAsync(IEnumerable<Scrobble> scrobble)
{
throw new NotImplementedException();
}
}
}

View File

@ -47,9 +47,12 @@
<Compile Include="Api\Helpers\ListResponse.cs" />
<Compile Include="Api\IAlbumApi.cs" />
<Compile Include="Api\IArtistApi.cs" />
<Compile Include="Api\ITrackApi.cs" />
<Compile Include="Api\IUserApi.cs" />
<Compile Include="Api\Enums\LastStatsTimeSpan.cs" />
<Compile Include="Api\Helpers\LastResponse{T}.cs" />
<Compile Include="Api\Scrobble.cs" />
<Compile Include="Api\TrackApi.cs" />
<Compile Include="Api\UserApi.cs" />
<Compile Include="Json\LastFmBooleanConverter.cs" />
<Compile Include="Api\IAuth.cs" />

View File

@ -83,6 +83,7 @@ public static string FormatQueryParameters(IEnumerable<KeyValuePair<string, stri
public static bool IsResponseValid(string json, out LastFmApiError error)
{
// hmmm
if (!json.Contains("error"))
{
error = LastFmApiError.None;

View File

@ -35,7 +35,7 @@ public class Album
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public static Album ParseJToken(JToken token)
internal static Album ParseJToken(JToken token)
{
var a = new Album();

View File

@ -12,7 +12,7 @@ public class Tag
#endregion
public static Tag ParseJToken(JToken token)
internal static Tag ParseJToken(JToken token)
{
var t = new Tag();

View File

@ -32,7 +32,7 @@ public class Track
/// <param name="token">A valid JToken</param>
/// <returns>track equivalent to the JToken</returns>
/// <remarks>If this method is used directly then the duration attribute will be parsed as MILLIseconds</remarks>
public static Track ParseJToken(JToken token)
internal static Track ParseJToken(JToken token)
{
var t = new Track();
@ -58,7 +58,7 @@ public static Track ParseJToken(JToken token)
/// <param name="albumName">Name of the album this track belongs to</param>
/// <returns>track equivalent to the JToken</returns>
/// <remarks>If this method is used then the duration attribute will be parsed as seconds</remarks>
public static Track ParseJToken(JToken token, string albumName)
internal static Track ParseJToken(JToken token, string albumName)
{
var t = ParseJToken(token);
t.AlbumName = albumName;

View File

@ -28,3 +28,6 @@
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
// Make this assembly unit testable
[assembly: InternalsVisibleTo("IF.Lastfm.Core.Tests")]