diff --git a/Jellyfin.Plugin.Lastfm/Api/BaseLastfmApiClient.cs b/Jellyfin.Plugin.Lastfm/Api/BaseLastfmApiClient.cs index 192220f..5d96efc 100644 --- a/Jellyfin.Plugin.Lastfm/Api/BaseLastfmApiClient.cs +++ b/Jellyfin.Plugin.Lastfm/Api/BaseLastfmApiClient.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Net.Http.Json; + using System.Text.Json.Serialization; using System.Linq; using System.Net.Http; using System.Text; @@ -23,7 +24,6 @@ public class BaseLastfmApiClient private readonly HttpClient _httpClient; private readonly ILogger _logger; - public BaseLastfmApiClient(IHttpClientFactory httpClientFactory, ILogger logger) { _httpClientFactory = httpClientFactory; @@ -45,14 +45,17 @@ public async Task Post(TRequest request) where T // Append the signature Helpers.AppendSignature(ref data); - using var requestMessage = new HttpRequestMessage(HttpMethod.Post, BuildPostUrl(request.Secure)); requestMessage.Content = new StringContent(SetPostData(data), Encoding.UTF8, "application/x-www-form-urlencoded"); using (var response = await _httpClient.SendAsync(requestMessage, CancellationToken.None)) { + var serializeOptions = new JsonSerializerOptions + { + NumberHandling = JsonNumberHandling.AllowReadingFromString + }; try { - var result = await response.Content.ReadFromJsonAsync(); + var result = await response.Content.ReadFromJsonAsync(serializeOptions); // Lets Log the error here to ensure all errors are logged if (result.IsError()) _logger.LogError(result.Message); @@ -61,7 +64,7 @@ public async Task Post(TRequest request) where T } catch (Exception e) { - _logger.LogDebug(e.Message); + _logger.LogError(e.Message); } } @@ -77,9 +80,13 @@ public async Task Get(TRequest request, Cancella { using (var response = await _httpClient.GetAsync(BuildGetUrl(request.ToDictionary(), request.Secure), cancellationToken)) { + var serializeOptions = new JsonSerializerOptions + { + NumberHandling = JsonNumberHandling.AllowReadingFromString + }; try { - var result = await response.Content.ReadFromJsonAsync(); + var result = await response.Content.ReadFromJsonAsync(serializeOptions); // Lets Log the error here to ensure all errors are logged if (result.IsError()) @@ -89,9 +96,8 @@ public async Task Get(TRequest request, Cancella } catch (Exception e) { - _logger.LogDebug(e.Message); + _logger.LogError(e.Message); } - return null; } } diff --git a/Jellyfin.Plugin.Lastfm/Configuration/configPage.html b/Jellyfin.Plugin.Lastfm/Configuration/configPage.html index 3a68291..087f139 100644 --- a/Jellyfin.Plugin.Lastfm/Configuration/configPage.html +++ b/Jellyfin.Plugin.Lastfm/Configuration/configPage.html @@ -1,10 +1,13 @@  + Last.fm Scrobbler Configuration + -
+
@@ -114,13 +117,13 @@ this.$el.find('#optionScrobble').prop('checked', data.Options.Scrobble); this.$el.find('#optionFavourite').prop('checked', data.Options.SyncFavourites); - //Dont forget to call checkboxradio('refresh') otherwise the UI wont update + // Don't forget to call checkboxradio('refresh') otherwise the UI wont update }, save: function (username, password) { var userConfig = this.getCurrentSelectedUser(); - //If the conig for the user doesnt exist, create one + //If the config for the user doesn't exist, create one if (!userConfig) { userConfig = $.extend({}, this.configDefaults, { MediaBrowserUserId: this.getCurrentSelectedUserId() }); @@ -143,10 +146,10 @@ //Get session with user data ApiClient.LastfmGetMobileSession(username, password).then($.proxy(function (data) { //Check if we have data - if (data && data.Session) { + if (data && data.session) { - userConfig.Username = data.Session.Name; - userConfig.SessionKey = data.Session.Key; + userConfig.Username = data.session.name; + userConfig.SessionKey = data.session.key; //Save this.doSave(); @@ -154,8 +157,8 @@ return; } - Dashboard.alert(data.Message || "Something went wrong"); - console.log(data.Message); + Dashboard.alert(data.message || "Something went wrong"); + console.log(data.message); }, this)); Dashboard.hideLoadingMsg(); }, @@ -253,4 +256,5 @@ };
- + + \ No newline at end of file diff --git a/Jellyfin.Plugin.Lastfm/Jellyfin.Plugin.Lastfm.csproj b/Jellyfin.Plugin.Lastfm/Jellyfin.Plugin.Lastfm.csproj index a4bb39f..88d88ea 100644 --- a/Jellyfin.Plugin.Lastfm/Jellyfin.Plugin.Lastfm.csproj +++ b/Jellyfin.Plugin.Lastfm/Jellyfin.Plugin.Lastfm.csproj @@ -2,15 +2,12 @@ net6.0 - 8.0.0.2 - 8.0.0.2 + 8.0.0.3 + 8.0.0.3 - - - @@ -18,7 +15,6 @@ - diff --git a/Jellyfin.Plugin.Lastfm/Models/MobileSession.cs b/Jellyfin.Plugin.Lastfm/Models/MobileSession.cs index 6523bec..284149c 100644 --- a/Jellyfin.Plugin.Lastfm/Models/MobileSession.cs +++ b/Jellyfin.Plugin.Lastfm/Models/MobileSession.cs @@ -1,17 +1,16 @@ namespace Jellyfin.Plugin.Lastfm.Models { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; - [DataContract] public class MobileSession { - [DataMember(Name = "name")] + [JsonPropertyName("name")] public string Name { get; set; } - [DataMember(Name = "key")] + [JsonPropertyName("key")] public string Key { get; set; } - [DataMember(Name = "subscriber")] + [JsonPropertyName("subscriber")] public int Subscriber { get; set; } } } diff --git a/Jellyfin.Plugin.Lastfm/Models/Responses/BaseResponse.cs b/Jellyfin.Plugin.Lastfm/Models/Responses/BaseResponse.cs index f1d21c3..6ea276e 100644 --- a/Jellyfin.Plugin.Lastfm/Models/Responses/BaseResponse.cs +++ b/Jellyfin.Plugin.Lastfm/Models/Responses/BaseResponse.cs @@ -1,14 +1,13 @@ namespace Jellyfin.Plugin.Lastfm.Models.Responses { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; - [DataContract] public class BaseResponse { - [DataMember(Name="message")] + [JsonPropertyName("message")] public string Message { get; set; } - [DataMember(Name="error")] + [JsonPropertyName("error")] public int ErrorCode { get; set; } public bool IsError() diff --git a/Jellyfin.Plugin.Lastfm/Models/Responses/GetTracksResponse.cs b/Jellyfin.Plugin.Lastfm/Models/Responses/GetTracksResponse.cs index 55a56fd..5c85932 100644 --- a/Jellyfin.Plugin.Lastfm/Models/Responses/GetTracksResponse.cs +++ b/Jellyfin.Plugin.Lastfm/Models/Responses/GetTracksResponse.cs @@ -1,12 +1,11 @@ namespace Jellyfin.Plugin.Lastfm.Models.Responses { using System.Collections.Generic; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; - [DataContract] public class GetTracksResponse : BaseResponse { - [DataMember(Name="tracks")] + [JsonPropertyName("tracks")] public GetTracksTracks Tracks { get; set; } public bool HasTracks() @@ -15,26 +14,24 @@ public bool HasTracks() } } - [DataContract] public class GetTracksTracks { - [DataMember(Name="track")] + [JsonPropertyName("track")] public List Tracks { get; set; } - [DataMember(Name = "@attr")] + [JsonPropertyName("@attr")] public GetTracksMeta Metadata { get; set; } } - [DataContract] public class GetTracksMeta { - [DataMember(Name = "totalPages")] + [JsonPropertyName("totalPages")] public int TotalPages { get; set; } - [DataMember(Name = "total")] + [JsonPropertyName("total")] public int TotalTracks { get; set; } - [DataMember(Name = "page")] + [JsonPropertyName("page")] public int Page { get; set; } public bool IsLastPage() diff --git a/Jellyfin.Plugin.Lastfm/Models/Responses/LovedTracksResponse.cs b/Jellyfin.Plugin.Lastfm/Models/Responses/LovedTracksResponse.cs index 46de7af..0aa8a4f 100644 --- a/Jellyfin.Plugin.Lastfm/Models/Responses/LovedTracksResponse.cs +++ b/Jellyfin.Plugin.Lastfm/Models/Responses/LovedTracksResponse.cs @@ -1,12 +1,11 @@ namespace Jellyfin.Plugin.Lastfm.Models.Responses { using System.Collections.Generic; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; - [DataContract] public class LovedTracksResponse : BaseResponse { - [DataMember(Name = "lovedtracks")] + [JsonPropertyName("lovedtracks")] public LovedTracks LovedTracks { get; set; } public bool HasLovedTracks() @@ -15,28 +14,26 @@ public bool HasLovedTracks() } } - [DataContract] public class LovedTracks { - [DataMember(Name = "track")] + [JsonPropertyName("track")] public List Tracks { get; set; } - [DataMember(Name = "@attr")] + [JsonPropertyName("@attr")] public LovedTracksMeta Metadata { get; set; } } - [DataContract] public class LovedTracksMeta { - [DataMember(Name = "totalPages")] + [JsonPropertyName("totalPages")] public int TotalPages { get; set; } - [DataMember(Name = "total")] + [JsonPropertyName("total")] public int TotalTracks { get; set; } - [DataMember(Name = "page")] + [JsonPropertyName("page")] public int Page { get; set; } public bool IsLastPage() diff --git a/Jellyfin.Plugin.Lastfm/Models/Responses/MobileSessionResponse.cs b/Jellyfin.Plugin.Lastfm/Models/Responses/MobileSessionResponse.cs index 187e092..b1013b8 100644 --- a/Jellyfin.Plugin.Lastfm/Models/Responses/MobileSessionResponse.cs +++ b/Jellyfin.Plugin.Lastfm/Models/Responses/MobileSessionResponse.cs @@ -1,11 +1,10 @@ namespace Jellyfin.Plugin.Lastfm.Models.Responses { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; - [DataContract] public class MobileSessionResponse : BaseResponse { - [DataMember(Name="session")] + [JsonPropertyName("session")] public MobileSession Session { get; set; } } } diff --git a/Jellyfin.Plugin.Lastfm/Models/Responses/ScrobbleResponse.cs b/Jellyfin.Plugin.Lastfm/Models/Responses/ScrobbleResponse.cs index 1dc667f..1885436 100644 --- a/Jellyfin.Plugin.Lastfm/Models/Responses/ScrobbleResponse.cs +++ b/Jellyfin.Plugin.Lastfm/Models/Responses/ScrobbleResponse.cs @@ -1,11 +1,10 @@ namespace Jellyfin.Plugin.Lastfm.Models.Responses { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; - [DataContract] public class ScrobbleResponse : BaseResponse { - [DataMember(Name = "scrobbles")] + [JsonPropertyName("scrobbles")] public Scrobbles Scrobbles { get; set; } } } diff --git a/Jellyfin.Plugin.Lastfm/Models/Scrobble.cs b/Jellyfin.Plugin.Lastfm/Models/Scrobble.cs index a4a4b8d..67cd16f 100644 --- a/Jellyfin.Plugin.Lastfm/Models/Scrobble.cs +++ b/Jellyfin.Plugin.Lastfm/Models/Scrobble.cs @@ -1,22 +1,19 @@ namespace Jellyfin.Plugin.Lastfm.Models { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; - //Wow what a bad response object! - [DataContract] public class Scrobbles { - [DataMember(Name = "@attr")] + [JsonPropertyName("@attr")] public ScrobbleAttributes Attributes { get; set; } } - [DataContract] public class ScrobbleAttributes { - [DataMember(Name = "accepted")] + [JsonPropertyName("accepted")] public bool Accepted { get; set; } - [DataMember(Name = "ignored")] + [JsonPropertyName("ignored")] public bool Ignored { get; set; } } } diff --git a/Jellyfin.Plugin.Lastfm/Models/Tracks.cs b/Jellyfin.Plugin.Lastfm/Models/Tracks.cs index f4ebb27..4fa7498 100644 --- a/Jellyfin.Plugin.Lastfm/Models/Tracks.cs +++ b/Jellyfin.Plugin.Lastfm/Models/Tracks.cs @@ -1,27 +1,25 @@ namespace Jellyfin.Plugin.Lastfm.Models { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; - [DataContract] public class BaseLastfmTrack { - [DataMember(Name="artist")] + [JsonPropertyName("artist")] public LastfmArtist Artist { get; set; } - [DataMember(Name = "name")] + [JsonPropertyName("name")] public string Name { get; set; } - [DataMember(Name = "mbid")] + [JsonPropertyName("mbid")] public string MusicBrainzId { get; set; } } - [DataContract] public class LastfmArtist { - [DataMember(Name="name")] + [JsonPropertyName("name")] public string Name { get; set; } - [DataMember(Name = "mbid")] + [JsonPropertyName("mbid")] public string MusicBrainzId { get; set; } } @@ -29,10 +27,10 @@ public class LastfmLovedTrack : BaseLastfmTrack { } - [DataContract] + public class LastfmTrack : BaseLastfmTrack { - [DataMember(Name="playcount")] + [JsonPropertyName("playcount")] public int PlayCount { get; set; } } } diff --git a/Jellyfin.Plugin.Lastfm/ScheduledTasks/ImportLastfmData.cs b/Jellyfin.Plugin.Lastfm/ScheduledTasks/ImportLastfmData.cs index e6c4c28..2861c9b 100644 --- a/Jellyfin.Plugin.Lastfm/ScheduledTasks/ImportLastfmData.cs +++ b/Jellyfin.Plugin.Lastfm/ScheduledTasks/ImportLastfmData.cs @@ -115,8 +115,13 @@ private async Task SyncDataforUserByArtistBulk(User user, IProgress prog return; } - // Group the list of loved tracks by artist - List> groupedLovedTracks = lovedTracks.GroupBy(t => t.Artist.MusicBrainzId).ToList(); + // remove any results from last fm loved tracks that do _not_ have an associated musicbrainz id + lovedTracks.RemoveAll(t => String.IsNullOrEmpty(t.Artist.MusicBrainzId)); + _logger.LogInformation("User {User} has {SongCount} loved tracks in last.fm that have an associated musicbrainz Artist id", user.Username, lovedTracks.Count); + if (lovedTracks.Count == 0) + return; + + var lovedTracksGroupedByArtist = lovedTracks.GroupBy(t => t.Artist.MusicBrainzId).ToDictionary(t => t.Key, t => t.ToList()); // Iterate over each artist in user's library // iterate over each song by artist @@ -126,17 +131,12 @@ private async Task SyncDataforUserByArtistBulk(User user, IProgress prog cancellationToken.ThrowIfCancellationRequested(); string artistMBid = Helpers.GetMusicBrainzArtistId(artist); - if (artistMBid == null) + if (String.IsNullOrEmpty(artistMBid)) continue; - if (groupedLovedTracks.FirstOrDefault(t => t.Key.Equals(artistMBid)) == null || !groupedLovedTracks.FirstOrDefault(t => t.Key.Equals(artistMBid)).Any()) - { + if (!lovedTracksGroupedByArtist.ContainsKey(artistMBid)) continue; - } - _logger.LogDebug("Found {0} LastFM lovedtracks for {1}", - groupedLovedTracks.FirstOrDefault(t => t.Key.Equals(artistMBid)).ToList().Count, - artist.Name); // Loop through each song foreach (Audio song in artist.GetTaggedItems(new InternalItemsQuery(user) { @@ -144,7 +144,18 @@ private async Task SyncDataforUserByArtistBulk(User user, IProgress prog EnableTotalRecordCount = false }).OfType