public async Task UpdatesNowPlaying() { var trackPlayed = DateTime.UtcNow.AddMinutes(-1); var testScrobble = new Scrobble(ARTIST_NAME, ALBUM_NAME, TRACK_NAME, trackPlayed) { Duration = new TimeSpan(0, 0, 3, 49, 200), AlbumArtist = ARTIST_NAME }; var response = await Lastfm.Track.UpdateNowPlayingAsync(testScrobble); Assert.IsTrue(response.Success); var tracks = await Lastfm.User.GetRecentScrobbles(Lastfm.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)); }
public async Task HandleResponseMultiple() { var command = new GetTracksCommand(MAuth.Object, "rj", "", "", DateTimeOffset.MinValue) { Count = 1 }; var expectedTrack = new LastTrack { ArtistName = "Stevie Wonder", Duration = new TimeSpan(0, 4, 8), PlayCount = 56, Mbid = "0161855d-0b98-4f2d-b2ab-446dbd8d6759", Name = "Superstition", ArtistMbid = "1ee18fb3-18a6-4c7f-8ba0-bc41cdd0462e", AlbumName = "Number Ones", Url = new Uri("http://www.last.fm/music/Stevie+Wonder/_/Superstition", UriKind.Absolute), Images = new LastImageSet( "http://userserve-ak.last.fm/serve/34s/99695819.jpg", "http://userserve-ak.last.fm/serve/64s/99695819.jpg", "http://userserve-ak.last.fm/serve/126/99695819.jpg", "http://userserve-ak.last.fm/serve/300x300/99695819.jpg") }; var response = CreateResponseMessage(Encoding.UTF8.GetString(LibraryApiResponses.LibraryGetTracksMultiple)); var actual = await command.HandleResponse(response); Assert.IsTrue(actual.Success); TestHelper.AssertSerialiseEqual(expectedTrack, actual.Content[1]); // Testing the second track returned }
public async Task HandleResponseSingle() { var command = new GetTracksCommand(MAuth.Object, "rj", "", "", DateTimeOffset.MinValue) { Count = 1 }; var expectedTrack = new LastTrack { ArtistName = "Dire Straits", Duration = new TimeSpan(0, 5, 47), PlayCount = 81, Mbid = "0317e524-7f70-4910-bc12-95dd468a29fc", Name = "Sultans of Swing", ArtistMbid = "614e3804-7d34-41ba-857f-811bad7c2b7a", AlbumName = "Dire Straits (Remastered)", Url = new Uri("http://www.last.fm/music/Dire+Straits/_/Sultans+of+Swing", UriKind.Absolute), Images = new LastImageSet( "http://userserve-ak.last.fm/serve/34s/56827829.jpg", "http://userserve-ak.last.fm/serve/64s/56827829.jpg", "http://userserve-ak.last.fm/serve/126/56827829.jpg", "http://userserve-ak.last.fm/serve/300x300/56827829.jpg") }; var response = CreateResponseMessage(Encoding.UTF8.GetString(LibraryApiResponses.LibraryGetTracksSingle)); var actual = await command.HandleResponse(response); Assert.IsTrue(actual.Success); TestHelper.AssertSerialiseEqual(expectedTrack, actual.Single()); }
public async Task HandleResponseSingle() { var command = new GetRecentTracksCommand(MAuth.Object, "rj") { Count = 1 }; var expectedTrack = new LastTrack { ArtistName = "Rick James", Mbid = "", Name = "Super Freak (Part 1) - 1981 Single Version", ArtistMbid = "cba9cec2-be8d-41bd-91b4-a1cd7de39b0c", TimePlayed = new DateTime(2014,12,20,10,16,52, DateTimeKind.Utc), AlbumName = "The Definitive Collection", Url = new Uri("http://www.last.fm/music/Rick+James/_/Super+Freak+(Part+1)+-+1981+Single+Version", UriKind.Absolute), Images = new LastImageSet( "http://userserve-ak.last.fm/serve/34s/90462319.jpg", "http://userserve-ak.last.fm/serve/64s/90462319.jpg", "http://userserve-ak.last.fm/serve/126/90462319.jpg", "http://userserve-ak.last.fm/serve/300x300/90462319.jpg") }; var response = CreateResponseMessage(Encoding.UTF8.GetString(UserApiResponses.UserGetRecentTracksSingle)); var actual = await command.HandleResponse(response); Assert.IsTrue(actual.Success); TestHelper.AssertSerialiseEqual(expectedTrack, actual.Single()); }
public async Task HandleResponseMultiple() { var command = new GetRecentTracksCommand(MAuth.Object, "rj") { Count = 1 }; var expectedTrack = new LastTrack { ArtistName = "The Who", TimePlayed = new DateTime(2014, 12, 19, 16, 13, 55,DateTimeKind.Utc), Mbid = "79f3dc97-2297-47ee-8556-9a1bb4b48d53", Name = "Pinball Wizard", ArtistMbid = "9fdaa16b-a6c4-4831-b87c-bc9ca8ce7eaa", AlbumName = "Tommy (Remastered)", Url = new Uri("http://www.last.fm/music/The+Who/_/Pinball+Wizard", UriKind.Absolute), Images = new LastImageSet( "http://userserve-ak.last.fm/serve/34s/35234991.jpg", "http://userserve-ak.last.fm/serve/64s/35234991.jpg", "http://userserve-ak.last.fm/serve/126/35234991.jpg", "http://userserve-ak.last.fm/serve/300x300/35234991.jpg") }; var response = CreateResponseMessage(Encoding.UTF8.GetString(UserApiResponses.UserGetRecentTracksMultiple)); var actual = await command.HandleResponse(response); Assert.IsTrue(actual.Success); TestHelper.AssertSerialiseEqual(expectedTrack, actual.Content[2]); }
public async Task ScrobblesSingle() { var trackPlayed = DateTimeOffset.UtcNow.AddMinutes(-1).RoundToNearestSecond(); var testScrobble = new Scrobble("Hot Chip", "The Warning", "Over and Over", trackPlayed) { AlbumArtist = ARTIST_NAME, ChosenByUser = false }; var response = await Lastfm.Scrobbler.ScrobbleAsync(testScrobble); Assert.IsTrue(response.Success); var testGuid = Guid.Empty; var expectedTrack = new LastTrack { Name = TRACK_NAME, ArtistName = ARTIST_NAME, AlbumName = ALBUM_NAME, Mbid = testGuid.ToString("D"), ArtistMbid = testGuid.ToString("D"), Url = new Uri("http://www.last.fm/music/Hot+Chip/_/Over+and+Over"), Images = new LastImageSet("http://userserve-ak.last.fm/serve/34s/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/300x300/50921593.png") }; var expectedJson = expectedTrack.TestSerialise(); var tracks = await Lastfm.User.GetRecentScrobbles(Lastfm.Auth.UserSession.Username, null, 0, 1); Assert.IsTrue(tracks.Any()); var actual = tracks.Content.First(); TestHelper.AssertSerialiseEqual(trackPlayed, actual.TimePlayed); actual.TimePlayed = null; // MBIDs returned by last.fm change from time to time, so let's just test that they're there. Assert.IsTrue(Guid.Parse(actual.Mbid) != Guid.Empty); Assert.IsTrue(Guid.Parse(actual.ArtistMbid) != Guid.Empty); actual.Mbid = testGuid.ToString("D"); actual.ArtistMbid = testGuid.ToString("D"); var actualJson = actual.TestSerialise(); Assert.AreEqual(expectedJson, actualJson, expectedJson.DifferencesTo(actualJson)); }
public static async Task<SaveResults> SaveTrackAsync(LastTrack track) { try { Song preparedSong = null; var providerId = track.ToSong().ProviderId; if (providerId == "lastid.") { preparedSong = await PrepareTrackForDownloadAsync(track); providerId = preparedSong.ProviderId; } var exists = App.Locator.CollectionService.SongAlreadyExists( providerId, track.Name, track.AlbumName, track.ArtistName); if (exists != null) { return new SaveResults() { Error = SavingError.AlreadyExists, Entry = exists }; } if (preparedSong == null) { preparedSong = await PrepareTrackForDownloadAsync(track); } await App.Locator.CollectionService.AddSongAsync(preparedSong).ConfigureAwait(false); CollectionHelper.MatchSong(preparedSong); return new SaveResults{Error = SavingError.None, Entry = preparedSong}; } catch (NetworkException) { return new SaveResults { Error = SavingError.Network }; } catch { return new SaveResults { Error = SavingError.Unknown }; } }
public async Task ScrobblesSingle() { var trackPlayed = DateTimeOffset.UtcNow.AddMinutes(-1).RoundToNearestSecond(); var testScrobble = new Scrobble("Hot Chip", "The Warning", "Over and Over", trackPlayed) { AlbumArtist = ARTIST_NAME, ChosenByUser = false }; var response = await Lastfm.Scrobbler.ScrobbleAsync(testScrobble); Assert.IsTrue(response.Success); var expectedTrack = new LastTrack { Name = TRACK_NAME, ArtistName = ARTIST_NAME, AlbumName = ALBUM_NAME }; var expectedJson = expectedTrack.TestSerialise(); var tracks = await Lastfm.User.GetRecentScrobbles(Lastfm.Auth.UserSession.Username, null, 1, 1); Assert.IsTrue(tracks.Any()); var actual = tracks.Content.First(); TestHelper.AssertSerialiseEqual(trackPlayed, actual.TimePlayed); actual.TimePlayed = null; // Some properties change from time to time; parsing is covered in unit tests actual.Mbid = null; actual.ArtistMbid = null; actual.Images = null; actual.Url = null; var actualJson = actual.TestSerialise(); Assert.AreEqual(expectedJson, actualJson, expectedJson.DifferencesTo(actualJson)); }
internal static async Task<Song> PrepareTrackForDownloadAsync(LastTrack track) { var lastTrack = await App.Locator.ScrobblerService.GetDetailTrack(track.Name, track.ArtistName).ConfigureAwait(false); LastArtist artist; if (lastTrack != null) track = lastTrack; var preparedSong = track.ToSong(); preparedSong.ArtistName = track.ArtistName; if (!string.IsNullOrEmpty(track.AlbumName + track.AlbumName)) { var lastAlbum = await App.Locator.ScrobblerService.GetDetailAlbum( track.AlbumName, track.ArtistName); artist = await App.Locator.ScrobblerService.GetDetailArtist(track.ArtistName).ConfigureAwait(false); if (lastAlbum != null) preparedSong.Album = lastAlbum.ToAlbum(); if (artist != null) preparedSong.Album.PrimaryArtist = artist.ToArtist(); } else { artist = await App.Locator.ScrobblerService.GetDetailArtist(track.ArtistName).ConfigureAwait(false); } if (artist != null) preparedSong.Artist = artist.ToArtist(); return preparedSong; }
/// <summary> /// Parses the given JToken into a track /// </summary> /// <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> internal static LastTrack ParseJToken(JToken token) { var t = new LastTrack(); t.Id = token.Value <string>("id"); t.Name = token.Value <string>("name"); t.Mbid = token.Value <string>("mbid"); //some tracks do not contain the playcount prop, it will throw a FormatException var playCountStr = token.Value <string>("playcount"); int playCount; if (int.TryParse(playCountStr, out playCount)) { t.PlayCount = playCount; } var listenerCountStr = token.Value <string>("listeners"); int listenerCount; if (int.TryParse(listenerCountStr, out listenerCount)) { t.ListenerCount = listenerCount; } var userPlayCountStr = token.Value <string>("userplaycount"); int userPlayCount; if (int.TryParse(userPlayCountStr, out userPlayCount)) { t.UserPlayCount = userPlayCount; } t.Url = new Uri(token.Value <string>("url"), UriKind.Absolute); var artistToken = token.SelectToken("artist"); if (artistToken.Type != JTokenType.String) { t.ArtistName = LastArtist.GetNameFromJToken(artistToken); t.ArtistMbid = artistToken.Value <string>("mbid"); } else { t.ArtistName = artistToken.ToObject <string>(); } var albumToken = token.SelectToken("album"); if (albumToken != null) { t.AlbumName = LastAlbum.GetNameFromJToken(albumToken); } var tagsToken = token.SelectToken("toptags"); if (tagsToken != null) { var tagToken = tagsToken.SelectToken("tag"); if (tagToken != null) { t.TopTags = tagToken.Type == JTokenType.Array ? tagToken.Children().Select(token1 => LastTag.ParseJToken(token1)) : new List <LastTag> { LastTag.ParseJToken(tagToken) }; } } var date = token.SelectToken("date"); if (date != null) { var stamp = date.Value <double>("uts"); t.TimePlayed = stamp.FromUnixTime(); } var images = token.SelectToken("image"); if (images != null) { var imageCollection = LastImageSet.ParseJToken(images); t.Images = imageCollection; } var lovedToken = token.SelectToken("userloved"); if (lovedToken != null) { t.IsLoved = Convert.ToBoolean(lovedToken.Value <int>()); } var attrToken = token.SelectToken("@attr"); if (attrToken != null && attrToken.HasValues) { t.IsNowPlaying = attrToken.Value <bool>("nowplaying"); t.Rank = attrToken.Value <int?>("rank"); } // api returns milliseconds when track.getInfo is called directly var secsStr = token.Value <string>("duration"); double secs; if (double.TryParse(secsStr, out secs)) { if (Math.Abs(secs - default(double)) > double.Epsilon) { t.Duration = TimeSpan.FromMilliseconds(secs); } } return(t); }
public static async Task<SaveResults> SaveTrackAsync(LastTrack track) { using ( var handle = Insights.TrackTime( "Save Song", new Dictionary<string, string> { {"Type", "LastFM"}, {"ProviderId", track.Id}, {"Name", track.Name}, {"ArtistName", track.ArtistName} })) { var result = await _SaveTrackAsync(track); ShowResults(result, track.Name); handle.Data.Add("SavingError", result.ToString()); return result; } }
private static async Task<SaveResults> _SaveTrackAsync(LastTrack track) { if (track == null) { return new SaveResults {Error = SavingError.Unknown}; } var alreadySaving = LastfmSavingTracks.FirstOrDefault(p => p == track.Id) != null; if (alreadySaving) { return new SaveResults { Error = SavingError.AlreadySaving }; } LastfmSavingTracks.Add(track.Id); while (!App.Locator.CollectionService.IsLibraryLoaded) { } var startTransaction = !App.Locator.SqlService.DbConnection.IsInTransaction; if (startTransaction) { App.Locator.SqlService.BeginTransaction(); } var result = await ScrobblerHelper.SaveTrackAsync(track); if (startTransaction) { App.Locator.SqlService.Commit(); } ShowErrorResults(result.Error, track.Name); LastfmSavingTracks.Remove(track.Id); #pragma warning disable 4014 if (result.Entry != null) { var song = (Song)result.Entry; if (!song.Album.HasArtwork && !song.Album.NoArtworkFound) { if (track.Images != null && track.Images.Largest != null) { SaveAlbumImageAsync(song.Album, track.Images.Largest.AbsoluteUri); } else { DownloadAlbumsArtworkAsync(); } } } DownloadArtistsArtworkAsync(); #pragma warning restore 4014 return result; }
internal static LastAlbum ParseJToken(JToken token) { var a = new LastAlbum(); a.Id = token.Value <string>("id"); var artistToken = token["artist"]; switch (artistToken.Type) { case JTokenType.String: a.ArtistName = token.Value <string>("artist"); break; case JTokenType.Object: a.ArtistName = artistToken.Value <string>("name"); break; } var tracksToken = token.SelectToken("tracks"); if (tracksToken != null) { var trackToken = tracksToken.SelectToken("track"); if (trackToken != null) { a.Tracks = trackToken.Type == JTokenType.Array ? trackToken.Children().Select(t => LastTrack.ParseJToken(t, a.Name)) : new List <LastTrack>() { LastTrack.ParseJToken(trackToken, a.Name) } } ; } else { a.Tracks = Enumerable.Empty <LastTrack>(); } var tagsToken = token.SelectToken("toptags"); if (tagsToken != null) { var tagToken = tagsToken.SelectToken("tag"); if (tagToken != null) { a.TopTags = tagToken.Type == JTokenType.Array ? tagToken.Children().Select(token1 => LastTag.ParseJToken(token1)) : new List <LastTag> { LastTag.ParseJToken(tagToken) }; } } else { a.TopTags = Enumerable.Empty <LastTag>(); } a.ListenerCount = token.Value <int?>("listeners"); a.Mbid = token.Value <string>("mbid"); a.Name = token.Value <string>("name"); var playCountStr = token.Value <string>("playcount"); int playCount; if (int.TryParse(playCountStr, out playCount)) { a.PlayCount = playCount; } var images = token.SelectToken("image"); if (images != null) { var imageCollection = LastImageSet.ParseJToken(images); a.Images = imageCollection; } a.Url = new Uri(token.Value <string>("url"), UriKind.Absolute); var dateString = token.Value <string>("releasedate"); DateTimeOffset releaseDate; if (DateTimeOffset.TryParse(dateString, out releaseDate)) { a.ReleaseDateUtc = releaseDate; } var wikiToken = token["wiki"]; if (wikiToken != null) { a.Wiki = LastWiki.ParseJToken(wikiToken); } return(a); }
/// <summary> /// Parses the given JToken into a track /// </summary> /// <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> internal static LastTrack ParseJToken(JToken token) { var t = new LastTrack(); t.Id = token.Value<string>("id"); t.Name = token.Value<string>("name"); t.Mbid = token.Value<string>("mbid"); //some tracks do not contain the playcount prop, it will throw a FormatException var playCountStr = token.Value<string>("playcount"); int playCount; if (int.TryParse(playCountStr, out playCount)) { t.PlayCount = playCount; } t.Url = new Uri(token.Value<string>("url"), UriKind.Absolute); var artistToken = token.SelectToken("artist"); if (artistToken.Type != JTokenType.String) { t.ArtistName = LastArtist.GetNameFromJToken(artistToken); t.ArtistMbid = artistToken.Value<string>("mbid"); } else t.ArtistName = artistToken.ToObject<string>(); var albumToken = token.SelectToken("album"); if (albumToken != null) { t.AlbumName = LastAlbum.GetNameFromJToken(albumToken); } var tagsToken = token.SelectToken("toptags"); if (tagsToken != null) { var tagToken = tagsToken.SelectToken("tag"); if (tagToken != null) { t.TopTags = tagToken.Type == JTokenType.Array ? tagToken.Children().Select(token1 => LastTag.ParseJToken(token1)) : new List<LastTag> { LastTag.ParseJToken(tagToken) }; } } var date = token.SelectToken("date"); if (date != null) { var stamp = date.Value<double>("uts"); t.TimePlayed = stamp.FromUnixTime(); } var images = token.SelectToken("image"); if (images != null) { var imageCollection = LastImageSet.ParseJToken(images); t.Images = imageCollection; } var lovedToken = token.SelectToken("userloved"); if (lovedToken != null) { t.IsLoved = Convert.ToBoolean(lovedToken.Value<int>()); } var attrToken = token.SelectToken("@attr"); if (attrToken != null && attrToken.HasValues) { t.IsNowPlaying = attrToken.Value<bool>("nowplaying"); t.Rank = attrToken.Value<int?>("rank"); } // api returns milliseconds when track.getInfo is called directly var secsStr = token.Value<string>("duration"); double secs; if (double.TryParse(secsStr, out secs)) { if (Math.Abs(secs - default(double)) > double.Epsilon) { t.Duration = TimeSpan.FromMilliseconds(secs); } } return t; }
/// <summary> /// Constructor. /// </summary> /// <param name="scrobble">The scrobbled track.</param> public FetchedFriendTrackViewModel(LastTrack scrobble) { Track = scrobble; }
/// <summary> /// Constructor. /// </summary> /// <param name="fetchedTrack">The fetched track.</param> /// <param name="smallAlbumImage">The small image of the parent album.</param> public FetchedTrackViewModel(LastTrack fetchedTrack, Uri smallAlbumImage) { FetchedTrack = fetchedTrack; SmallAlbumImage = smallAlbumImage; }