public Scrobble(string trackData) { List <string> trackDataList = trackData.Split('-').ToList(); if (trackDataList.Count > 0) { string artist = trackDataList[0]; string title = string.Empty; for (int i = 1; i < trackDataList.Count; i++) { title += trackDataList[i]; } Scrobbler scrobbler = new Scrobbler("0a5674077da2782718075412eab00800", "56668ad9e4293be48def8f5ab1a6c658"); Track track = new Track { ArtistName = artist, TrackName = title }; ScrobbleResponse response = scrobbler.Scrobble(track); ErrorMessage = response.Exception.Message + " " + response.ErrorCode.ToString(); } else { ErrorMessage = trackDataList.Count.ToString(); } }
// Public method for sending the specified media as Scrobbles to the API public async Task <ScrobbleResponse> SendScrobbles(List <MediaItem> mediaItems) { ScrobbleResponse response = null; // Create an empty parameter base var baseParameters = new Dictionary <string, string>(); int mediaItemCount = 0; // Iterate each of the media items foreach (MediaItem mediaItem in mediaItems) { // De-serialization for some reason causes a null entry to be injected if (mediaItem != null) { // Adds the basic details of the media as an array baseParameters.Add($"artist[{mediaItemCount}]", mediaItem.ArtistName); baseParameters.Add($"album[{mediaItemCount}]", mediaItem.AlbumName); if (!string.IsNullOrEmpty(mediaItem.AlbumArtist)) { baseParameters.Add($"albumArtist[{mediaItemCount}]", mediaItem.AlbumArtist); } baseParameters.Add($"track[{mediaItemCount}]", mediaItem.TrackName); baseParameters.Add($"duration[{mediaItemCount}]", mediaItem.TrackLength.ToString()); baseParameters.Add($"timestamp[{mediaItemCount}]", UnixTimeStampHelper.GetUnixTimeStampFromDateTime(mediaItem.StartedPlaying).ToString("#0")); ++mediaItemCount; } } // Add the required authentication parameters AddRequiredRequestParams(baseParameters, "track.scrobble", _sessionToken.Key); // Convert the parameters into body content FormUrlEncodedContent postContent = new FormUrlEncodedContent(baseParameters); // Post the scrobbles and get the result var rawResponse = await Post <JObject>("track.scrobble", postContent, baseParameters.ToArray()).ConfigureAwait(false); #if DebugAPICalls Console.WriteLine($"Sent Scrobble request, response:\r\n {rawResponse}"); #endif // Use a dedicate method to get the scrobble response because the API does NOT correctly return the response // in a correct JSON formatted manner return(GetScrobbleResponseFromScrobble(rawResponse)); }
// Private method for converting an incorrectly formatted scrobble response into a correctly formatted response from this client private ScrobbleResponse GetScrobbleResponseFromScrobble(JObject scrobbleResponse) { // De-serializing the scrobble response seems to be hit and miss if you follow 'normal convention'. // JsonConvert.Deserialize fails to de-serialize the scrobble response with a 'Path scrobbles.scrobble.artist' error. // However, splitting out the JSON string from the root node, and converting the objects into the relevant types // seems to solve the problem nicely. From these response we can then build up the overall scrobble response, setting // the relevant flags. ScrobbleResponse response = new ScrobbleResponse(); List <Scrobble> scrobbleResults = new List <Scrobble>(); if (scrobbleResponse["scrobbles"]["scrobble"] is JArray) { // Convert the scrobble responses into an array Scrobble[] scrobbles = JsonConvert.DeserializeObject <Scrobble[]>(scrobbleResponse["scrobbles"]["scrobble"].ToString()); if (scrobbles != null) { scrobbleResults.AddRange(scrobbles.ToList()); } } else { Scrobble scrobble = JsonConvert.DeserializeObject <Scrobble>(scrobbleResponse["scrobbles"]["scrobble"].ToString()); if (scrobble != null) { scrobbleResults.Add(scrobble); } } // Parse the results, and set the relevant properties int ignored = scrobbleResults.Count(item => item.IgnoredMessage.Code != Enums.ReasonCodes.IgnoredReason.AllOk); int accepted = scrobbleResults.Count(item => item.IgnoredMessage.Code == Enums.ReasonCodes.IgnoredReason.AllOk); response.Scrobbles = new Scrobbles() { AcceptedResult = new Models.AcceptedResult() { Ignored = ignored, Accepted = accepted }, ScrobbleItems = scrobbleResults.ToArray() }; return(response); }
/// <summary> /// Scrobbles the given <paramref name="scrobbles"/>. /// </summary> /// <param name="scrobbles">Collection of scrobble objects to scrobble.</param> /// <param name="needCaching">If true, scrobbles should be cached /// if they can't be scrobbled.</param> /// <returns>Response.</returns> public async Task <ScrobbleResponse> ScrobbleAsync(IEnumerable <Scrobble> scrobbles, bool needCaching = false) { User.UpdateRecentScrobbles(); if (User.RecentScrobbles.Count + scrobbles.Count() > User.MAXSCROBBLESPERDAY) { throw new InvalidOperationException($"Scrobbling these tracks would break the daily scrobble cap! " + $"({User.MAXSCROBBLESPERDAY})\r\nYou have scrobbled {User.RecentScrobbles.Count} tracks in the past 24 hours."); } ScrobbleResponse response = needCaching ? await _cachingScrobbler.ScrobbleAsync(scrobbles) : await _scrobbler.ScrobbleAsync(scrobbles); if (response.Success && response.Status == LastResponseStatus.Successful) { User.AddScrobbles(scrobbles, DateTime.Now); } return(response); }
// Method for passing the results of a successful scrobble to the user interface // 'Successful' being that the API accepted the scrobble request, even though it may have reject some for given reasons private static void ShowScrobbleResult(ScrobbleResponse scrobbleResult) { if (Core.Settings.ShowNotifications && (Core.Settings.ShowScrobbleNotifications == null || Core.Settings.ShowScrobbleNotifications == true)) { int successfulScrobbleCount = scrobbleResult.Scrobbles.AcceptedResult.Accepted; //int ignoredScrobbles = scrobbleResult.Scrobbles.AcceptedResult.Ignored; //string resultText = (successfulScrobbleCount > 0) ? $"Accepted: {successfulScrobbleCount}" : ""; //resultText += !string.IsNullOrEmpty(resultText) && ignoredScrobbles > 0 ? ", " : ""; //resultText += (ignoredScrobbles > 0) ? $"Ignored: {ignoredScrobbles}" : ""; //string balloonText = $"Successfully scrobbled {scrobbleResult.Scrobbles.ScrobbleItems.Count()} track(s).\r\n{resultText}"; if (successfulScrobbleCount > 0) { string balloonText = string.Format(LocalizationStrings.PopupNotifications_ScrobbleSuccess, scrobbleResult.Scrobbles.ScrobbleItems.Count()); _uiThread.ShowNotification(Core.APPLICATION_TITLE, balloonText); } } }
protected ScrobbleResponses GetScrobbleResponsesFromNavigator(XPathNavigator navigator) { var responses = new ScrobbleResponses { AcceptedCount = ApiHelper.SelectSingleNode(navigator, "/lfm/scrobbles/@accepted").ValueAsInt, IgnoredCount = ApiHelper.SelectSingleNode(navigator, "/lfm/scrobbles/@ignored").ValueAsInt }; foreach (XPathNavigator item in navigator.Select("/lfm/scrobbles/scrobble")) { var response = new ScrobbleResponse { IgnoredMessageCode = ApiHelper.SelectSingleNode(item, "ignoredMessage/@code").ValueAsInt, IgnoredMessage = ApiHelper.SelectSingleNode(item, "ignoredMessage").Value, Track = GetCorrectedTrack(item) }; responses.Add(response); } return(responses); }
protected ScrobbleResponses GetScrobbleResponsesFromNavigator(XPathNavigator navigator) { var responses = new ScrobbleResponses { AcceptedCount = ApiHelper.SelectSingleNode(navigator, "/lfm/scrobbles/@accepted").ValueAsInt, IgnoredCount = ApiHelper.SelectSingleNode(navigator, "/lfm/scrobbles/@ignored").ValueAsInt }; foreach (XPathNavigator item in navigator.Select("/lfm/scrobbles/scrobble")) { var response = new ScrobbleResponse { IgnoredMessageCode = ApiHelper.SelectSingleNode(item, "ignoredMessage/@code").ValueAsInt, IgnoredMessage = ApiHelper.SelectSingleNode(item, "ignoredMessage").Value, Track = GetCorrectedTrack(item) }; responses.Add(response); } return responses; }
// Method used to tell the Scrobbler to attempt to scrobble and cached, or queued media items if scrobbling is enabled private static async Task CheckScrobbleState() { List <IScrobbleSource> sourcesToScrobbleFrom = null; if (_scrobblingActive) { // Tell the Ui that the scrobbler is scrobbling _uiThread.SetStatus(LocalizationStrings.NotificationThread_Status_CheckingScrobbleStatus); List <MediaItem> sourceMedia = new List <MediaItem>(); // Load any of the cached media items sourceMedia = await LoadCachedScrobbles().ConfigureAwait(false); // Retrieve from each of the enabled plugins, any media that has been queued but not scrobbled foreach (IScrobbleSource source in ScrobblePlugins?.Where(plugin => plugin.IsEnabled).ToList()) { List <MediaItem> pluginMedia = source.MediaToScrobble; sourceMedia.AddRange(pluginMedia); // Clear the plugins queued media source.ClearQueuedMedia(); } // Communicate with the API to get the current user details from the session and only attempt // scrobble if there is communication with the API if (await CanScrobble().ConfigureAwait(false)) { if (sourceMedia != null && sourceMedia.Any()) { Console.WriteLine($"Scrobbling {sourceMedia.Count} item(s)...."); // Notify the user interface that there are a number of media items about to be scrobbled _uiThread.SetStatus(string.Format(LocalizationStrings.NotificationThread_Status_Scrobbling, sourceMedia.Count)); try { // Scrobbling must ONLY send up to 50 items at a time, but we don't want to notify the user interface on // each batch processed. This list is used to batch up the result of each batch sent to the API ScrobbleResponse overallScrobbleResult = new ScrobbleResponse() { Scrobbles = new Scrobbles() { AcceptedResult = new AcceptedResult(), ScrobbleItems = new Scrobble[] {} } }; do { // Ensure the media is split up into groups of 50 List <MediaItem> scrobbleBatch = sourceMedia.Take(50).ToList(); Logger.FileLogger.Write(_logFilePathAndFilename, "Scrobble Tracking", $"Scrobbling {scrobbleBatch.Count} item(s) for scrobbling..."); // Send the scrobbles to the API ScrobbleResponse scrobbleResult = await _lastFMClient.SendScrobbles(scrobbleBatch).ConfigureAwait(false); // Only remove the items in the batch AFTER a successful scrobble request is sent // so that we can cache the full media list without manipulating the lists again. sourceMedia.RemoveRange(0, scrobbleBatch.Count); // Check the response from LastFM, and cache anything where the API Limit was exceeded CacheFailedItems(scrobbleResult.Scrobbles.ScrobbleItems.ToList()); // Track the result of the scrobble overallScrobbleResult.Scrobbles.AcceptedResult.Accepted = scrobbleResult.Scrobbles.AcceptedResult.Accepted; overallScrobbleResult.Scrobbles.AcceptedResult.Ignored = scrobbleResult.Scrobbles.AcceptedResult.Ignored; // Append the current scrobble results to the overall batch List <Scrobble> scrobbledItems = overallScrobbleResult.Scrobbles.ScrobbleItems.ToList(); scrobbledItems.AddRange(scrobbleResult.Scrobbles.ScrobbleItems.ToList()); overallScrobbleResult.Scrobbles.ScrobbleItems = scrobbledItems.ToArray(); }while (sourceMedia.Count > 0); // Now push the scrobble result back to the user interface ShowScrobbleResult(overallScrobbleResult); } catch (Exception ex) { Logger.FileLogger.Write(_logFilePathAndFilename, "Scrobble Tracking", $"Failed to send scrobbles to Last.fm due to an error: {ex}"); // The API wasn't available. Cache the media so we can try again. CacheOfflineItems(sourceMedia); // Show the result of the scrobble ShowScrobbleResult(sourceMedia); } } } else { // The API wasn't available. Cache the media so we can try again. CacheOfflineItems(sourceMedia); } // Tell the user interface to show the current scrobble state (in case the user has gone offline) _uiThread?.ShowScrobbleState(); } }