예제 #1
0
        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();
            }
        }
예제 #2
0
        // 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));
        }
예제 #3
0
        // 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);
        }
예제 #4
0
        /// <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);
                }
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
        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();
            }
        }