From 600814d09f6e31ef0d706b8bf2be555d0538d135 Mon Sep 17 00:00:00 2001 From: Rikki Tooley Date: Fri, 14 Jun 2013 16:19:26 +0100 Subject: [PATCH] Scrobble method! Also making all ParseJToken methods internal, but using [assembly:InternalsVisibleTo("IF.Lastfm.Core.Tests")] to make them testable --- IF.Lastfm.Core/Api/AlbumApi.cs | 4 +- IF.Lastfm.Core/Api/Auth.cs | 8 +-- IF.Lastfm.Core/Api/Helpers/ApiExtensions.cs | 10 ++++ IF.Lastfm.Core/Api/IAuth.cs | 1 + IF.Lastfm.Core/Api/ITrackApi.cs | 15 ++++++ IF.Lastfm.Core/Api/Scrobble.cs | 28 ++++++++++ IF.Lastfm.Core/Api/TrackApi.cs | 58 +++++++++++++++++++++ IF.Lastfm.Core/IF.Lastfm.Core.csproj | 3 ++ IF.Lastfm.Core/LastFm.cs | 1 + IF.Lastfm.Core/Objects/Album.cs | 2 +- IF.Lastfm.Core/Objects/Tag.cs | 2 +- IF.Lastfm.Core/Objects/Track.cs | 4 +- IF.Lastfm.Core/Properties/AssemblyInfo.cs | 3 ++ 13 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 IF.Lastfm.Core/Api/ITrackApi.cs create mode 100644 IF.Lastfm.Core/Api/Scrobble.cs create mode 100644 IF.Lastfm.Core/Api/TrackApi.cs diff --git a/IF.Lastfm.Core/Api/AlbumApi.cs b/IF.Lastfm.Core/Api/AlbumApi.cs index b677921..18d3a5a 100644 --- a/IF.Lastfm.Core/Api/AlbumApi.cs +++ b/IF.Lastfm.Core/Api/AlbumApi.cs @@ -93,12 +93,12 @@ public Task> GetShoutsForAlbumWithMbidAsync(string mbid, boo #region album.getTags - public Task> GetUserTagsForAlbumAsync(string artist, string album, bool autocorrect = false) + public Task> GetUserTagsForAlbumAsync(string artist, string album, string username, bool autocorrect = false) { throw new NotImplementedException(); } - public Task> GetUserTagsForAlbumWithMbidAsync(string mbid, bool autocorrect = false) + public Task> GetUserTagsForAlbumWithMbidAsync(string mbid, string username, bool autocorrect = false) { throw new NotImplementedException(); } diff --git a/IF.Lastfm.Core/Api/Auth.cs b/IF.Lastfm.Core/Api/Auth.cs index 20ee86f..77ac9bf 100644 --- a/IF.Lastfm.Core/Api/Auth.cs +++ b/IF.Lastfm.Core/Api/Auth.cs @@ -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 GetSessionTokenAsync(string username, string password) + public async Task 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 + var postContent = LastFm.CreatePostBody(apiMethod, ApiKey, ApiSignature, new Dictionary { {"password", password}, {"username", username} diff --git a/IF.Lastfm.Core/Api/Helpers/ApiExtensions.cs b/IF.Lastfm.Core/Api/Helpers/ApiExtensions.cs index 70d20ec..0038a58 100644 --- a/IF.Lastfm.Core/Api/Helpers/ApiExtensions.cs +++ b/IF.Lastfm.Core/Api/Helpers/ApiExtensions.cs @@ -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; + } } } \ No newline at end of file diff --git a/IF.Lastfm.Core/Api/IAuth.cs b/IF.Lastfm.Core/Api/IAuth.cs index b626b02..80fb63c 100644 --- a/IF.Lastfm.Core/Api/IAuth.cs +++ b/IF.Lastfm.Core/Api/IAuth.cs @@ -9,6 +9,7 @@ public interface IAuth bool HasAuthenticated { get; } string ApiKey { get; } UserSession User { get; } + string ApiSignature { get; } /// /// Gets the session token which is used as authentication for any service calls. diff --git a/IF.Lastfm.Core/Api/ITrackApi.cs b/IF.Lastfm.Core/Api/ITrackApi.cs new file mode 100644 index 0000000..0c9a22c --- /dev/null +++ b/IF.Lastfm.Core/Api/ITrackApi.cs @@ -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 ScrobbleAsync(Scrobble scrobble); + Task ScrobbleAsync(IEnumerable scrobble); + } +} \ No newline at end of file diff --git a/IF.Lastfm.Core/Api/Scrobble.cs b/IF.Lastfm.Core/Api/Scrobble.cs new file mode 100644 index 0000000..4fd256b --- /dev/null +++ b/IF.Lastfm.Core/Api/Scrobble.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/IF.Lastfm.Core/Api/TrackApi.cs b/IF.Lastfm.Core/Api/TrackApi.cs new file mode 100644 index 0000000..3f9f7b7 --- /dev/null +++ b/IF.Lastfm.Core/Api/TrackApi.cs @@ -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 ScrobbleAsync(Scrobble scrobble) + { + const string apiMethod = "track.scrobble"; + + var postContent = LastFm.CreatePostBody(apiMethod, + Auth.ApiKey, + Auth.ApiSignature, + new Dictionary + { + {"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 ScrobbleAsync(IEnumerable scrobble) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/IF.Lastfm.Core/IF.Lastfm.Core.csproj b/IF.Lastfm.Core/IF.Lastfm.Core.csproj index d5f10c2..1fe4c0f 100644 --- a/IF.Lastfm.Core/IF.Lastfm.Core.csproj +++ b/IF.Lastfm.Core/IF.Lastfm.Core.csproj @@ -47,9 +47,12 @@ + + + diff --git a/IF.Lastfm.Core/LastFm.cs b/IF.Lastfm.Core/LastFm.cs index dee00b4..f49a346 100644 --- a/IF.Lastfm.Core/LastFm.cs +++ b/IF.Lastfm.Core/LastFm.cs @@ -83,6 +83,7 @@ public static string FormatQueryParameters(IEnumerable /// /// - public static Album ParseJToken(JToken token) + internal static Album ParseJToken(JToken token) { var a = new Album(); diff --git a/IF.Lastfm.Core/Objects/Tag.cs b/IF.Lastfm.Core/Objects/Tag.cs index dbf802f..5b3a491 100644 --- a/IF.Lastfm.Core/Objects/Tag.cs +++ b/IF.Lastfm.Core/Objects/Tag.cs @@ -12,7 +12,7 @@ public class Tag #endregion - public static Tag ParseJToken(JToken token) + internal static Tag ParseJToken(JToken token) { var t = new Tag(); diff --git a/IF.Lastfm.Core/Objects/Track.cs b/IF.Lastfm.Core/Objects/Track.cs index 5ff563b..54abf8e 100644 --- a/IF.Lastfm.Core/Objects/Track.cs +++ b/IF.Lastfm.Core/Objects/Track.cs @@ -32,7 +32,7 @@ public class Track /// A valid JToken /// track equivalent to the JToken /// If this method is used directly then the duration attribute will be parsed as MILLIseconds - 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) /// Name of the album this track belongs to /// track equivalent to the JToken /// If this method is used then the duration attribute will be parsed as seconds - public static Track ParseJToken(JToken token, string albumName) + internal static Track ParseJToken(JToken token, string albumName) { var t = ParseJToken(token); t.AlbumName = albumName; diff --git a/IF.Lastfm.Core/Properties/AssemblyInfo.cs b/IF.Lastfm.Core/Properties/AssemblyInfo.cs index 84a3eea..a96c08d 100644 --- a/IF.Lastfm.Core/Properties/AssemblyInfo.cs +++ b/IF.Lastfm.Core/Properties/AssemblyInfo.cs @@ -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")]