From f44a27a32742d469b81626b88ffc2026fb82c927 Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:01:31 +0100 Subject: [PATCH 01/10] Update `MetaBrainz.Build.Sdk` to v3.1.2 --- MetaBrainz.ListenBrainz/MetaBrainz.ListenBrainz.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MetaBrainz.ListenBrainz/MetaBrainz.ListenBrainz.csproj b/MetaBrainz.ListenBrainz/MetaBrainz.ListenBrainz.csproj index 405a16f..e26dfef 100644 --- a/MetaBrainz.ListenBrainz/MetaBrainz.ListenBrainz.csproj +++ b/MetaBrainz.ListenBrainz/MetaBrainz.ListenBrainz.csproj @@ -1,7 +1,7 @@ - + Zastai From aeea89a7e227a9027e60044b4885bbc3de18a1be Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:01:41 +0100 Subject: [PATCH 02/10] Add several terms to the dictionary --- MetaBrainz.ListenBrainz.sln.DotSettings | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/MetaBrainz.ListenBrainz.sln.DotSettings b/MetaBrainz.ListenBrainz.sln.DotSettings index 648ec09..7928cb0 100644 --- a/MetaBrainz.ListenBrainz.sln.DotSettings +++ b/MetaBrainz.ListenBrainz.sln.DotSettings @@ -61,5 +61,13 @@ True True True + True + True True - True + True + True + True + True + True + True + True From 475269e4c59e4c0ed3149ff93a9c2a7c07b98eb5 Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:01:54 +0100 Subject: [PATCH 03/10] Tweak the README This adjusts the link aliases and adds a section linking to the GitHub release notes. --- README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9b0fd8e..e7eab47 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,25 @@ # MetaBrainz.ListenBrainz [![Build Status][CI-S]][CI-L] [![NuGet Version][NuGet-S]][NuGet-L] -This is a library providing access to the [ListenBrainz API][LB-API]. +This is a library providing access to the +[ListenBrainz API][api-reference]. -[ListenBrainz][LB] keeps track of users' listens of music tracks -(similar to sites like [last.fm][LastFM] and [libre.fm][LibreFM]). +[ListenBrainz][home] keeps track of users' listens of music tracks +(similar to sites like [last.fm][last-fm] and [libre.fm][libre-fm]). [CI-S]: https://github.com/Zastai/MetaBrainz.ListenBrainz/actions/workflows/build.yml/badge.svg [CI-L]: https://github.com/Zastai/MetaBrainz.ListenBrainz/actions/workflows/build.yml + [NuGet-S]: https://img.shields.io/nuget/v/MetaBrainz.ListenBrainz [NuGet-L]: https://nuget.org/packages/MetaBrainz.ListenBrainz -[LB]: https://listenbrainz.org/ -[LB-API]: https://listenbrainz.readthedocs.io/en/latest/dev/api.html -[LastFM]: https://www.last.fm -[LibreFM]: https://libre.fm +[api-reference]: https://listenbrainz.readthedocs.io/en/latest/users/api +[home]: https://listenbrainz.org/ + +[last-fm]: https://www.last.fm +[libre-fm]: https://libre.fm + +## Release Notes + +These are available [on GitHub][release-notes]. + +[release-notes]: https://github.com/Zastai/MetaBrainz.ListenBrainz/releases From b1fba25cebb655ad34a0c09587782429d0da811c Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:09:52 +0100 Subject: [PATCH 04/10] Update `MetaBrainz.Common` to v3.0.0 This drops `QueryException` in favor of `HttpError` from that library. All uses of the obsolete `UnixTime` class have been replaced with corresponding functionality of `DateTimeOffset`. --- Directory.Packages.props | 1 + .../Json/Readers/ListenTimeRangeReader.cs | 13 +- .../Readers/SiteArtistStatisticsReader.cs | 15 +- .../Json/Readers/UserArtistMapReader.cs | 15 +- .../Readers/UserArtistStatisticsReader.cs | 15 +- .../Json/Readers/UserDailyActivityReader.cs | 15 +- .../Readers/UserListeningActivityReader.cs | 15 +- .../Readers/UserRecordingStatisticsReader.cs | 15 +- .../Readers/UserReleaseStatisticsReader.cs | 15 +- .../Json/Writers/ListenWriter.cs | 3 +- MetaBrainz.ListenBrainz/ListenBrainz.cs | 151 ++++++++---------- .../MetaBrainz.ListenBrainz.csproj | 1 + .../Objects/FetchedListens.cs | 3 +- .../Objects/LatestImport.cs | 3 +- MetaBrainz.ListenBrainz/Objects/Listen.cs | 3 +- .../Objects/SubmittedListen.cs | 5 +- MetaBrainz.ListenBrainz/QueryException.cs | 26 --- .../MetaBrainz.ListenBrainz.net6.0.cs.md | 14 -- .../MetaBrainz.ListenBrainz.net8.0.cs.md | 14 -- 19 files changed, 150 insertions(+), 192 deletions(-) delete mode 100644 MetaBrainz.ListenBrainz/QueryException.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 8aa96ac..dc06dc1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,6 +4,7 @@ + diff --git a/MetaBrainz.ListenBrainz/Json/Readers/ListenTimeRangeReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/ListenTimeRangeReader.cs index e62493e..de1c551 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/ListenTimeRangeReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/ListenTimeRangeReader.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text.Json; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.Common.Json.Converters; using MetaBrainz.ListenBrainz.Objects; @@ -24,18 +23,22 @@ protected override ListenTimeRange ReadObjectContents(ref Utf8JsonReader reader, try { reader.Read(); switch (prop) { - case "from_ts": - rangeStart = UnixTime.Convert(reader.GetOptionalInt64()); + case "from_ts": { + var unixTime = reader.GetOptionalInt64(); + rangeStart = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "listen_count": listenCount = reader.GetInt32(); break; case "time_range": description = reader.GetString(); break; - case "to_ts": - rangeEnd = UnixTime.Convert(reader.GetOptionalInt64()); + case "to_ts": { + var unixTime = reader.GetOptionalInt64(); + rangeEnd = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } default: rest ??= new Dictionary(); rest[prop] = reader.GetOptionalObject(options); diff --git a/MetaBrainz.ListenBrainz/Json/Readers/SiteArtistStatisticsReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/SiteArtistStatisticsReader.cs index bc12f98..cb905d4 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/SiteArtistStatisticsReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/SiteArtistStatisticsReader.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text.Json; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; using MetaBrainz.ListenBrainz.Objects; @@ -33,11 +32,13 @@ protected override SiteArtistStatistics ReadPayload(ref Utf8JsonReader reader, J case "count": count = reader.GetInt32(); break; - case "from_ts": - oldestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "from_ts": { + var unixTime = reader.GetOptionalInt64(); + oldestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "last_updated": - lastUpdated = UnixTime.Convert(reader.GetInt64()); + lastUpdated = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()); break; case "offset": offset = reader.GetInt32(); @@ -48,9 +49,11 @@ protected override SiteArtistStatistics ReadPayload(ref Utf8JsonReader reader, J goto default; // also register it as an unhandled property } break; - case "to_ts": - newestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "to_ts": { + var unixTime = reader.GetOptionalInt64(); + newestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } default: rest ??= new Dictionary(); rest[prop] = reader.GetOptionalObject(options); diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserArtistMapReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserArtistMapReader.cs index 30af233..4876bdb 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserArtistMapReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserArtistMapReader.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text.Json; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; using MetaBrainz.ListenBrainz.Objects; @@ -29,11 +28,13 @@ protected override UserArtistMap ReadPayload(ref Utf8JsonReader reader, JsonSeri case "artist_map": countries = reader.ReadList(ArtistCountryInfoReader.Instance, options); break; - case "from_ts": - oldestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "from_ts": { + var unixTime = reader.GetOptionalInt64(); + oldestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "last_updated": - lastUpdated = UnixTime.Convert(reader.GetInt64()); + lastUpdated = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()); break; case "range": range = EnumHelper.ParseStatisticsRange(reader.GetString()); @@ -41,9 +42,11 @@ protected override UserArtistMap ReadPayload(ref Utf8JsonReader reader, JsonSeri goto default; // also register it as an unhandled property } break; - case "to_ts": - newestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "to_ts": { + var unixTime = reader.GetOptionalInt64(); + newestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "user_id": user = reader.GetString(); break; diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserArtistStatisticsReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserArtistStatisticsReader.cs index f4ec7ee..c2b9898 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserArtistStatisticsReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserArtistStatisticsReader.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text.Json; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; using MetaBrainz.ListenBrainz.Objects; @@ -35,11 +34,13 @@ protected override UserArtistStatistics ReadPayload(ref Utf8JsonReader reader, J case "count": count = reader.GetInt32(); break; - case "from_ts": - oldestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "from_ts": { + var unixTime = reader.GetOptionalInt64(); + oldestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "last_updated": - lastUpdated = UnixTime.Convert(reader.GetInt64()); + lastUpdated = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()); break; case "offset": offset = reader.GetInt32(); @@ -50,9 +51,11 @@ protected override UserArtistStatistics ReadPayload(ref Utf8JsonReader reader, J goto default; // also register it as an unhandled property } break; - case "to_ts": - newestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "to_ts": { + var unixTime = reader.GetOptionalInt64(); + newestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "total_artist_count": totalCount = reader.GetInt32(); break; diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserDailyActivityReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserDailyActivityReader.cs index 738cb43..f0a44c6 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserDailyActivityReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserDailyActivityReader.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text.Json; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; using MetaBrainz.ListenBrainz.Objects; @@ -29,11 +28,13 @@ protected override UserDailyActivity ReadPayload(ref Utf8JsonReader reader, Json case "daily_activity": activity = reader.GetOptionalObject(DailyActivityReader.Instance, options); break; - case "from_ts": - oldestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "from_ts": { + var unixTime = reader.GetOptionalInt64(); + oldestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "last_updated": - lastUpdated = UnixTime.Convert(reader.GetInt64()); + lastUpdated = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()); break; case "range": range = EnumHelper.ParseStatisticsRange(reader.GetString()); @@ -41,9 +42,11 @@ protected override UserDailyActivity ReadPayload(ref Utf8JsonReader reader, Json goto default; // also register it as an unhandled property } break; - case "to_ts": - newestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "to_ts": { + var unixTime = reader.GetOptionalInt64(); + newestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "user_id": user = reader.GetString(); break; diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserListeningActivityReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserListeningActivityReader.cs index e54b21f..d2f28af 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserListeningActivityReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserListeningActivityReader.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text.Json; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; using MetaBrainz.ListenBrainz.Objects; @@ -26,11 +25,13 @@ protected override UserListeningActivity ReadPayload(ref Utf8JsonReader reader, try { reader.Read(); switch (prop) { - case "from_ts": - oldestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "from_ts": { + var unixTime = reader.GetOptionalInt64(); + oldestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "last_updated": - lastUpdated = UnixTime.Convert(reader.GetInt64()); + lastUpdated = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()); break; case "listening_activity": activity = reader.ReadList(ListenTimeRangeReader.Instance, options); @@ -41,9 +42,11 @@ protected override UserListeningActivity ReadPayload(ref Utf8JsonReader reader, goto default; // also register it as an unhandled property } break; - case "to_ts": - newestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "to_ts": { + var unixTime = reader.GetOptionalInt64(); + newestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "user_id": user = reader.GetString(); break; diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserRecordingStatisticsReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserRecordingStatisticsReader.cs index a242ab5..507ce63 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserRecordingStatisticsReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserRecordingStatisticsReader.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text.Json; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; using MetaBrainz.ListenBrainz.Objects; @@ -35,11 +34,13 @@ protected override UserRecordingStatistics ReadPayload(ref Utf8JsonReader reader case "count": count = reader.GetInt32(); break; - case "from_ts": - oldestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "from_ts": { + var unixTime = reader.GetOptionalInt64(); + oldestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "last_updated": - lastUpdated = UnixTime.Convert(reader.GetInt64()); + lastUpdated = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()); break; case "offset": offset = reader.GetInt32(); @@ -50,9 +51,11 @@ protected override UserRecordingStatistics ReadPayload(ref Utf8JsonReader reader goto default; // also register it as an unhandled property } break; - case "to_ts": - newestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "to_ts": { + var unixTime = reader.GetOptionalInt64(); + newestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "total_recording_count": totalCount = reader.GetInt32(); break; diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserReleaseStatisticsReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserReleaseStatisticsReader.cs index 240f672..c600a69 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserReleaseStatisticsReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserReleaseStatisticsReader.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text.Json; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; using MetaBrainz.ListenBrainz.Objects; @@ -35,11 +34,13 @@ protected override UserReleaseStatistics ReadPayload(ref Utf8JsonReader reader, case "count": count = reader.GetInt32(); break; - case "from_ts": - oldestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "from_ts": { + var unixTime = reader.GetOptionalInt64(); + oldestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "last_updated": - lastUpdated = UnixTime.Convert(reader.GetInt64()); + lastUpdated = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()); break; case "offset": offset = reader.GetInt32(); @@ -50,9 +51,11 @@ protected override UserReleaseStatistics ReadPayload(ref Utf8JsonReader reader, goto default; // also register it as an unhandled property } break; - case "to_ts": - newestListen = UnixTime.Convert(reader.GetOptionalInt64()); + case "to_ts": { + var unixTime = reader.GetOptionalInt64(); + newestListen = unixTime is null ? null : DateTimeOffset.FromUnixTimeSeconds(unixTime.Value); break; + } case "total_release_count": totalCount = reader.GetInt32(); break; diff --git a/MetaBrainz.ListenBrainz/Json/Writers/ListenWriter.cs b/MetaBrainz.ListenBrainz/Json/Writers/ListenWriter.cs index 9d9e578..3fb12e6 100644 --- a/MetaBrainz.ListenBrainz/Json/Writers/ListenWriter.cs +++ b/MetaBrainz.ListenBrainz/Json/Writers/ListenWriter.cs @@ -1,6 +1,5 @@ using System.Text.Json; -using MetaBrainz.Common; using MetaBrainz.Common.Json.Converters; using MetaBrainz.ListenBrainz.Interfaces; @@ -11,7 +10,7 @@ internal sealed class ListenWriter : ObjectWriter { public static readonly ListenWriter Instance = new(); protected override void WriteObjectContents(Utf8JsonWriter writer, ISubmittedListen value, JsonSerializerOptions options) { - writer.WriteNumber("listened_at", UnixTime.Convert(value.Timestamp)); + writer.WriteNumber("listened_at", value.Timestamp.ToUnixTimeSeconds()); writer.WritePropertyName("track_metadata"); TrackInfoWriter.Instance.Write(writer, value.Track, options); } diff --git a/MetaBrainz.ListenBrainz/ListenBrainz.cs b/MetaBrainz.ListenBrainz/ListenBrainz.cs index 21b74b2..23acb8a 100644 --- a/MetaBrainz.ListenBrainz/ListenBrainz.cs +++ b/MetaBrainz.ListenBrainz/ListenBrainz.cs @@ -284,12 +284,12 @@ public Task GetLatestImportAsync(string user, CancellationToken c /// for . /// public void SetLatestImport(string user, DateTimeOffset timestamp) - => AsyncUtils.ResultOf(this.SetLatestImportAsync(user, UnixTime.Convert(timestamp))); + => AsyncUtils.ResultOf(this.SetLatestImportAsync(user, timestamp.ToUnixTimeSeconds())); /// Set the timestamp of the newest listen submitted by a user in previous imports to ListenBrainz. /// The MusicBrainz ID of the user whose data is needed. /// - /// The timestamp to set, expressed as the number of seconds since the Unix time epoch. + /// The timestamp to set, expressed as the number of seconds since the Unix time epoch. /// /// /// This will access the POST /1/latest-import endpoint and requires to be set to the token @@ -308,12 +308,12 @@ public void SetLatestImport(string user, DateTimeOffset timestamp) /// https://listenbrainz.org/profile/. /// public Task SetLatestImportAsync(string user, DateTimeOffset timestamp, CancellationToken cancellationToken = default) - => this.SetLatestImportAsync(user, UnixTime.Convert(timestamp), cancellationToken); + => this.SetLatestImportAsync(user, timestamp.ToUnixTimeSeconds(), cancellationToken); /// Set the timestamp of the newest listen submitted by a user in previous imports to ListenBrainz. /// The MusicBrainz ID of the user whose data is needed. /// - /// The timestamp to set, expressed as the number of seconds since the Unix time epoch. + /// The timestamp to set, expressed as the number of seconds since the Unix time epoch. /// /// The cancellation token to cancel the operation. /// A task that will perform the operation. @@ -874,7 +874,7 @@ public void SubmitSingleListen(DateTimeOffset timestamp, string track, string ar /// /// /// The date and time at which the track was listened to, expressed as the number of seconds since - /// the Unix time epoch. + /// the Unix time epoch. /// /// The name of the track being listened to. /// The name of the artist performing the track being listened to. @@ -939,7 +939,7 @@ public Task SubmitSingleListenAsync(DateTimeOffset timestamp, string track, stri /// /// /// The date and time at which the track was listened to, expressed as the number of seconds since - /// the Unix time epoch. + /// the Unix time epoch. /// /// The name of the track being listened to. /// The name of the artist performing the track being listened to. @@ -1065,8 +1065,9 @@ public Task GetListensAsync(string user, int? count = null, int /// Gets the most recent listens for a user, starting from a particular timestamp. /// The MusicBrainz ID of the user whose data is needed. /// - /// The timestamp to start from, expressed as the number of seconds since the Unix time epoch. - /// Returned listens will have a timestamp greater than, but not including, this value. + /// The timestamp to start from, expressed as the number of seconds since + /// the Unix time epoch. Returned listens will have a timestamp greater than, but not + /// including, this value. /// /// /// The (maximum) number of listens to return; must be no greater than .
@@ -1096,13 +1097,14 @@ public IFetchedListens GetListensAfter(string user, long after, int? count = nul /// /// The requested listens, in descending timestamp order. public IFetchedListens GetListensAfter(string user, DateTimeOffset after, int? count = null, int? timeRange = null) - => this.PerformGetListens(user, UnixTime.Convert(after), null, count, timeRange); + => this.PerformGetListens(user, after.ToUnixTimeSeconds(), null, count, timeRange); /// Gets the most recent listens for a user, starting from a particular timestamp. /// The MusicBrainz ID of the user whose data is needed. /// - /// The timestamp to start from, expressed as the number of seconds since the Unix time epoch. - /// Returned listens will have a timestamp greater than, but not including, this value. + /// The timestamp to start from, expressed as the number of seconds since + /// the Unix time epoch. Returned listens will have a timestamp greater than, but not + /// including, this value. /// /// /// The (maximum) number of listens to return; must be no greater than .
@@ -1136,7 +1138,7 @@ public Task GetListensAfterAsync(string user, long after, int? /// The requested listens, in descending timestamp order. public Task GetListensAfterAsync(string user, DateTimeOffset after, int? count = null, int? timeRange = null, CancellationToken cancellationToken = default) - => this.PerformGetListensAsync(user, UnixTime.Convert(after), null, count, timeRange, cancellationToken); + => this.PerformGetListensAsync(user, after.ToUnixTimeSeconds(), null, count, timeRange, cancellationToken); #endregion @@ -1145,8 +1147,9 @@ public Task GetListensAfterAsync(string user, DateTimeOffset af /// Gets historical listens for a user, starting from a particular timestamp. /// The MusicBrainz ID of the user whose data is needed. /// - /// The timestamp to start from, expressed as the number of seconds since the Unix time epoch. - /// Returned listens will have a timestamp less than, but not including, this value. + /// The timestamp to start from, expressed as the number of seconds since + /// the Unix time epoch. Returned listens will have a timestamp less than, but not + /// including, this value. /// /// /// The (maximum) number of listens to return; must be no greater than .
@@ -1176,13 +1179,14 @@ public IFetchedListens GetListensBefore(string user, long before, int? count = n /// /// The requested listens, in descending timestamp order. public IFetchedListens GetListensBefore(string user, DateTimeOffset before, int? count = null, int? timeRange = null) - => this.PerformGetListens(user, null, UnixTime.Convert(before), count, timeRange); + => this.PerformGetListens(user, null, before.ToUnixTimeSeconds(), count, timeRange); /// Gets historical listens for a user, starting from a particular timestamp. /// The MusicBrainz ID of the user whose data is needed. /// - /// The timestamp to start from, expressed as the number of seconds since the Unix time epoch. - /// Returned listens will have a timestamp less than, but not including, this value. + /// The timestamp to start from, expressed as the number of seconds since + /// the Unix time epoch. Returned listens will have a timestamp less than, but not + /// including, this value. /// /// /// The (maximum) number of listens to return; must be no greater than .
@@ -1216,7 +1220,7 @@ public Task GetListensBeforeAsync(string user, long before, int /// The requested listens, in descending timestamp order. public Task GetListensBeforeAsync(string user, DateTimeOffset before, int? count = null, int? timeRange = null, CancellationToken cancellationToken = default) - => this.PerformGetListensAsync(user, null, UnixTime.Convert(before), count, timeRange, cancellationToken); + => this.PerformGetListensAsync(user, null, before.ToUnixTimeSeconds(), count, timeRange, cancellationToken); #endregion @@ -1225,12 +1229,14 @@ public Task GetListensBeforeAsync(string user, DateTimeOffset b /// Gets the listens for a user in a specific timespan. /// The MusicBrainz ID of the user whose data is needed. /// - /// The timestamp to start from, expressed as the number of seconds since the Unix time epoch. - /// Returned listens will have a timestamp greater than, but not including, this value. + /// The timestamp to start from, expressed as the number of seconds since + /// the Unix time epoch. Returned listens will have a timestamp greater than, but not + /// including, this value. /// /// - /// The timestamp to end at, expressed as the number of seconds since the Unix time epoch. - /// Returned listens will have a timestamp less than, but not including, this value. + /// The timestamp to end at, expressed as the number of seconds since + /// the Unix time epoch. Returned listens will have a timestamp less than, but not + /// including, this value. /// /// /// The (maximum) number of listens to return; must be no greater than .
@@ -1256,17 +1262,19 @@ public IFetchedListens GetListensBetween(string user, long after, long before, i /// /// The requested listens, in descending timestamp order. public IFetchedListens GetListensBetween(string user, DateTimeOffset after, DateTimeOffset before, int? count = null) - => this.PerformGetListens(user, UnixTime.Convert(after), UnixTime.Convert(before), count); + => this.PerformGetListens(user, after.ToUnixTimeSeconds(), before.ToUnixTimeSeconds(), count); /// Gets the listens for a user in a specific timespan. /// The MusicBrainz ID of the user whose data is needed. /// - /// The timestamp to start from, expressed as the number of seconds since the Unix time epoch. - /// Returned listens will have a timestamp greater than, but not including, this value. + /// The timestamp to start from, expressed as the number of seconds since + /// the Unix time epoch. Returned listens will have a timestamp greater than, but not + /// including, this value. /// /// - /// The timestamp to end at, expressed as the number of seconds since the Unix time epoch. - /// Returned listens will have a timestamp less than, but not including, this value. + /// The timestamp to end at, expressed as the number of seconds since + /// the Unix time epoch. Returned listens will have a timestamp less than, but not + /// including, this value. /// /// /// The (maximum) number of listens to return; must be no greater than .
@@ -1296,7 +1304,7 @@ public Task GetListensBetweenAsync(string user, long after, lon /// The requested listens, in descending timestamp order. public Task GetListensBetweenAsync(string user, DateTimeOffset after, DateTimeOffset before, int? count = null, CancellationToken cancellationToken = default) - => this.PerformGetListensAsync(user, UnixTime.Convert(after), UnixTime.Convert(before), count, null, cancellationToken); + => this.PerformGetListensAsync(user, after.ToUnixTimeSeconds(), before.ToUnixTimeSeconds(), count, null, cancellationToken); #endregion @@ -1507,10 +1515,6 @@ private async Task GetAsync(string address, IDi where TInterface : class where TObject : class, TInterface { var response = await this.PerformRequestAsync(address, HttpMethod.Get, null, options, cancellationToken).ConfigureAwait(false); - // FIXME: Should this use IsSuccessStatusCode? If so, which one(s) should attempt to process the response content? - if (response.StatusCode != HttpStatusCode.OK) { - throw ListenBrainz.CreateQueryExceptionFor(response); - } var task = JsonUtils.GetJsonContentAsync(response, ListenBrainz.JsonReaderOptions, cancellationToken); return await task.ConfigureAwait(false); } @@ -1523,10 +1527,6 @@ private async Task GetAsync(string address, IDi if (response.StatusCode == HttpStatusCode.NoContent) { return null; } - // FIXME: Should this use IsSuccessStatusCode? If so, which one(s) should attempt to process the response content? - if (response.StatusCode != HttpStatusCode.OK) { - throw ListenBrainz.CreateQueryExceptionFor(response); - } var task = JsonUtils.GetJsonContentAsync(response, ListenBrainz.JsonReaderOptions, cancellationToken); return await task.ConfigureAwait(false); } @@ -1560,7 +1560,7 @@ private async Task PerformRequestAsync(string address, Http break; } default: - throw new QueryException(HttpStatusCode.MethodNotAllowed, $"Unsupported method: {method}"); + throw new HttpError(HttpStatusCode.MethodNotAllowed, $"Unsupported method: {method}"); } var response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); Debug.Print($"[{DateTime.UtcNow}] => RESPONSE: {(int) response.StatusCode}/{response.StatusCode} '{response.ReasonPhrase}' " + @@ -1576,7 +1576,39 @@ private async Task PerformRequestAsync(string address, Http finally { this._rateLimitLock.ExitWriteLock(); } - return response; + try { + return await response.EnsureSuccessfulAsync(cancellationToken).ConfigureAwait(false); + } + catch (HttpError error) { + // If we get an error with content that can be interpreted as an ErrorInfo structure, wrap it in an error containing that info + if (!string.IsNullOrEmpty(error.Content)) { + ErrorInfo? ei; + try { + ei = JsonSerializer.Deserialize(error.Content, ListenBrainz.JsonReaderOptions); + if (ei is null) { + throw new JsonException("Error info was null."); + } + } + catch (Exception e) { + Debug.Print($"[{DateTime.UtcNow}] => FAILED TO PARSE ERROR RESPONSE CONTENT AS JSON: {e.Message}"); + ei = null; + } + if (ei is not null) { + var reason = error.Reason; + if (ei.Code != (int) response.StatusCode) { + Debug.Print($"[{DateTime.UtcNow}] => ERROR CODE ({ei.Code}) DOES NOT MATCH HTTP STATUS CODE!"); + reason = "Error"; + } + if (ei.UnhandledProperties is not null) { + foreach (var prop in ei.UnhandledProperties) { + Debug.Print($"[{DateTime.UtcNow}] => UNEXPECTED ERROR PROPERTY: {prop.Key} -> {prop.Value}"); + } + } + throw new HttpError((HttpStatusCode) ei.Code, reason, response.Version, ei.Error, error); + } + } + throw; + } } private Task PostAsync(string address, T content, IDictionary? options, @@ -1586,11 +1618,8 @@ private Task PostAsync(string address, T content, IDictionary private async Task PostAsync(string address, string body, IDictionary? options, CancellationToken cancellationToken = default) { var response = await this.PerformRequestAsync(address, HttpMethod.Post, body, options, cancellationToken).ConfigureAwait(false); - if (!response.IsSuccessStatusCode) { - throw ListenBrainz.CreateQueryExceptionFor(response); - } #if DEBUG - var content = await HttpUtils.GetStringContentAsync(response, cancellationToken).ConfigureAwait(false); + var content = await response.GetStringContentAsync(cancellationToken).ConfigureAwait(false); Debug.Print($"[{DateTime.UtcNow}] => RESPONSE TEXT: {TextUtils.FormatMultiLine(content)}"); #endif } @@ -1599,44 +1628,6 @@ private async Task PostAsync(string address, string body, IDictionary 0) { - errorInfo = AsyncUtils.ResultOf(HttpUtils.GetStringContentAsync(response)); - if (string.IsNullOrWhiteSpace(errorInfo)) { - Debug.Print($"[{DateTime.UtcNow}] => NO ERROR RESPONSE TEXT"); - errorInfo = null; - } - else { - Debug.Print($"[{DateTime.UtcNow}] => ERROR RESPONSE TEXT: {TextUtils.FormatMultiLine(errorInfo)}"); - } - } - else { - Debug.Print($"[{DateTime.UtcNow}] => NO ERROR RESPONSE CONTENT"); - } - if (errorInfo != null) { - try { - var ei = JsonSerializer.Deserialize(errorInfo, ListenBrainz.JsonReaderOptions); - if (ei is null) { - throw new JsonException("Error info was null."); - } - errorInfo = ei.Error; - if (ei.Code != (int) response.StatusCode) { - Debug.Print($"[{DateTime.UtcNow}] => ERROR CODE ({ei.Code}) DOES NOT MATCH HTTP STATUS CODE!"); - } - if (ei.UnhandledProperties != null) { - foreach (var prop in ei.UnhandledProperties) { - Debug.Print($"[{DateTime.UtcNow}] => UNEXPECTED ERROR PROPERTY: {prop.Key} -> {prop.Value}"); - } - } - } - catch (Exception e) { - Debug.Print($"[{DateTime.UtcNow}] => FAILED TO PARSE AS JSON ({e.Message}); USING AS-IS"); - } - } - return new QueryException(response.StatusCode, response.ReasonPhrase, errorInfo); - } - private static Uri GetDefaultContactInfo() => ListenBrainz.DefaultContactInfo ?? throw new InvalidOperationException($"Contact info must have been set using {nameof(ListenBrainz.DefaultContactInfo)}."); diff --git a/MetaBrainz.ListenBrainz/MetaBrainz.ListenBrainz.csproj b/MetaBrainz.ListenBrainz/MetaBrainz.ListenBrainz.csproj index e26dfef..9dd019e 100644 --- a/MetaBrainz.ListenBrainz/MetaBrainz.ListenBrainz.csproj +++ b/MetaBrainz.ListenBrainz/MetaBrainz.ListenBrainz.csproj @@ -20,6 +20,7 @@ + diff --git a/MetaBrainz.ListenBrainz/Objects/FetchedListens.cs b/MetaBrainz.ListenBrainz/Objects/FetchedListens.cs index 05b1a55..b4da439 100644 --- a/MetaBrainz.ListenBrainz/Objects/FetchedListens.cs +++ b/MetaBrainz.ListenBrainz/Objects/FetchedListens.cs @@ -3,7 +3,6 @@ using JetBrains.Annotations; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; @@ -14,7 +13,7 @@ internal sealed class FetchedListens : JsonBasedObject, IFetchedListens { public FetchedListens(IReadOnlyList listens, long ts, string user) { this.Listens = listens; - this.Timestamp = UnixTime.Convert(ts); + this.Timestamp = DateTimeOffset.FromUnixTimeSeconds(ts); this.UnixTimestamp = ts; this.User = user; } diff --git a/MetaBrainz.ListenBrainz/Objects/LatestImport.cs b/MetaBrainz.ListenBrainz/Objects/LatestImport.cs index 160679b..1ef1582 100644 --- a/MetaBrainz.ListenBrainz/Objects/LatestImport.cs +++ b/MetaBrainz.ListenBrainz/Objects/LatestImport.cs @@ -2,7 +2,6 @@ using JetBrains.Annotations; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; @@ -12,7 +11,7 @@ namespace MetaBrainz.ListenBrainz.Objects; internal sealed class LatestImport : JsonBasedObject, ILatestImport { public LatestImport(long ts, string user) { - this.Timestamp = UnixTime.Convert(ts); + this.Timestamp = DateTimeOffset.FromUnixTimeSeconds(ts); this.UnixTimestamp = ts; this.User = user; } diff --git a/MetaBrainz.ListenBrainz/Objects/Listen.cs b/MetaBrainz.ListenBrainz/Objects/Listen.cs index 9c061b3..6a818cf 100644 --- a/MetaBrainz.ListenBrainz/Objects/Listen.cs +++ b/MetaBrainz.ListenBrainz/Objects/Listen.cs @@ -2,7 +2,6 @@ using JetBrains.Annotations; -using MetaBrainz.Common; using MetaBrainz.Common.Json; using MetaBrainz.ListenBrainz.Interfaces; @@ -14,7 +13,7 @@ internal sealed class Listen : JsonBasedObject, IListen { public Listen(string inserted, Guid msid, long timestamp, ITrackInfo track, string user) { this.InsertedAt = inserted; this.MessyRecordingId = msid; - this.Timestamp = UnixTime.Convert(timestamp); + this.Timestamp = DateTimeOffset.FromUnixTimeSeconds(timestamp); this.Track = track; this.UnixTimestamp = timestamp; this.User = user; diff --git a/MetaBrainz.ListenBrainz/Objects/SubmittedListen.cs b/MetaBrainz.ListenBrainz/Objects/SubmittedListen.cs index 0fa6227..59cb264 100644 --- a/MetaBrainz.ListenBrainz/Objects/SubmittedListen.cs +++ b/MetaBrainz.ListenBrainz/Objects/SubmittedListen.cs @@ -2,7 +2,6 @@ using JetBrains.Annotations; -using MetaBrainz.Common; using MetaBrainz.ListenBrainz.Interfaces; namespace MetaBrainz.ListenBrainz.Objects; @@ -24,13 +23,13 @@ public SubmittedListen(DateTimeOffset timestamp, string track, string artist, st /// Creates a new listen. /// /// The date and time at which the track was listened to, expressed as the number of seconds since - /// the Unix time epoch. + /// the Unix time epoch. /// /// The listened track's name. /// The listened track's artist. /// The listened track's release. public SubmittedListen(long timestamp, string track, string artist, string? release = null) : base(track, artist, release) { - this.Timestamp = UnixTime.Convert(timestamp); + this.Timestamp = DateTimeOffset.FromUnixTimeSeconds(timestamp); } /// Creates a new listen, using the current (UTC) date and time as timestamp. diff --git a/MetaBrainz.ListenBrainz/QueryException.cs b/MetaBrainz.ListenBrainz/QueryException.cs deleted file mode 100644 index c4644dd..0000000 --- a/MetaBrainz.ListenBrainz/QueryException.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Net; - -namespace MetaBrainz.ListenBrainz; - -/// An error reported by the ListenBrainz web service. -public sealed class QueryException : Exception { - - /// The HTTP status code for the exception. - public readonly HttpStatusCode Code; - - /// The reason phrase for the exception, if available. - public readonly string? Reason; - - /// Creates a new instance. - /// The HTTP message code for the error. - /// The reason phrase for the error. - /// A further error message. - /// The exception that caused the error (if any). - public QueryException(HttpStatusCode code, string? reason = null, string? message = null, Exception? cause = null) - : base(message ?? reason, cause) { - this.Code = code; - this.Reason = reason; - } - -} diff --git a/public-api/MetaBrainz.ListenBrainz.net6.0.cs.md b/public-api/MetaBrainz.ListenBrainz.net6.0.cs.md index b9e1495..10b4358 100644 --- a/public-api/MetaBrainz.ListenBrainz.net6.0.cs.md +++ b/public-api/MetaBrainz.ListenBrainz.net6.0.cs.md @@ -267,20 +267,6 @@ public sealed class ListenBrainz : System.IDisposable { } ``` -### Type: QueryException - -```cs -public sealed class QueryException : System.Exception { - - public readonly System.Net.HttpStatusCode Code; - - public readonly string? Reason; - - public QueryException(System.Net.HttpStatusCode code, string? reason = null, string? message = null, System.Exception? cause = null); - -} -``` - ### Type: StatisticsRange ```cs diff --git a/public-api/MetaBrainz.ListenBrainz.net8.0.cs.md b/public-api/MetaBrainz.ListenBrainz.net8.0.cs.md index 0cbe695..c49f85b 100644 --- a/public-api/MetaBrainz.ListenBrainz.net8.0.cs.md +++ b/public-api/MetaBrainz.ListenBrainz.net8.0.cs.md @@ -267,20 +267,6 @@ public sealed class ListenBrainz : System.IDisposable { } ``` -### Type: QueryException - -```cs -public sealed class QueryException : System.Exception { - - public readonly System.Net.HttpStatusCode Code; - - public readonly string? Reason; - - public QueryException(System.Net.HttpStatusCode code, string? reason = null, string? message = null, System.Exception? cause = null); - -} -``` - ### Type: StatisticsRange ```cs From dd4fc662820097cbf1d5aecb58dabb419f803b46 Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:10:38 +0100 Subject: [PATCH 05/10] Update `MetaBrainz.Common.Json` to v6.0.1 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index dc06dc1..827553b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,7 +5,7 @@ - +
From 7d0055ba41bed38ab41b2dd6027e71523df92cb9 Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:11:21 +0100 Subject: [PATCH 06/10] Add nullability attribute --- MetaBrainz.ListenBrainz/Json/EnumHelper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MetaBrainz.ListenBrainz/Json/EnumHelper.cs b/MetaBrainz.ListenBrainz/Json/EnumHelper.cs index 135e20d..2648bf4 100644 --- a/MetaBrainz.ListenBrainz/Json/EnumHelper.cs +++ b/MetaBrainz.ListenBrainz/Json/EnumHelper.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace MetaBrainz.ListenBrainz.Json; internal static class EnumHelper { + [return: NotNullIfNotNull(nameof(text))] public static StatisticsRange? ParseStatisticsRange(string? text) { if (text == null) { return null; From 359d42bbf1e017d3a4c826b316d6d96d5de34d02 Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:16:08 +0100 Subject: [PATCH 07/10] Apply style tweaks These include: - use of `is [not] null` instead of `==/!= null` or `.HasValue` - target-typed `new` - expression-valued method bodies - patterns --- MetaBrainz.ListenBrainz/Json/EnumHelper.cs | 18 +++++++-------- .../Json/Readers/ArtistCountryInfoReader.cs | 6 ++--- .../Json/Readers/ArtistInfoReader.cs | 4 ++-- .../Json/Readers/ErrorInfoReader.cs | 4 ++-- .../Json/Readers/FetchedListensReader.cs | 4 ++-- .../Json/Readers/HourlyActivityReader.cs | 4 ++-- .../Json/Readers/LatestImportReader.cs | 4 ++-- .../Json/Readers/ListenCountReader.cs | 2 +- .../Json/Readers/ListenReader.cs | 10 ++++----- .../Json/Readers/ListenTimeRangeReader.cs | 4 ++-- .../Readers/MusicBrainzIdMappingsReader.cs | 2 +- .../Json/Readers/PayloadReader.cs | 2 +- .../Json/Readers/PlayingNowReader.cs | 10 ++++----- .../Json/Readers/PlayingTrackReader.cs | 2 +- .../Json/Readers/RecentListensReader.cs | 2 +- .../Json/Readers/RecordingInfoReader.cs | 4 ++-- .../Json/Readers/ReleaseInfoReader.cs | 4 ++-- .../Readers/SiteArtistStatisticsReader.cs | 8 +++---- .../Readers/TokenValidationResultReader.cs | 4 ++-- .../Json/Readers/TrackInfoReader.cs | 6 ++--- .../Json/Readers/UserArtistMapReader.cs | 6 ++--- .../Readers/UserArtistStatisticsReader.cs | 10 ++++----- .../Json/Readers/UserDailyActivityReader.cs | 6 ++--- .../Readers/UserListeningActivityReader.cs | 6 ++--- .../Readers/UserRecordingStatisticsReader.cs | 6 ++--- .../Readers/UserReleaseStatisticsReader.cs | 6 ++--- .../Json/Writers/TrackInfoWriter.cs | 4 ++-- MetaBrainz.ListenBrainz/ListenBrainz.cs | 22 +++++++++---------- .../Objects/AdditionalInfo.cs | 14 ++++++------ .../Objects/MusicBrainzIdMappings.cs | 2 +- .../Objects/SubmissionPayload.cs | 2 +- 31 files changed, 93 insertions(+), 95 deletions(-) diff --git a/MetaBrainz.ListenBrainz/Json/EnumHelper.cs b/MetaBrainz.ListenBrainz/Json/EnumHelper.cs index 2648bf4..2ad6dd0 100644 --- a/MetaBrainz.ListenBrainz/Json/EnumHelper.cs +++ b/MetaBrainz.ListenBrainz/Json/EnumHelper.cs @@ -7,7 +7,7 @@ internal static class EnumHelper { [return: NotNullIfNotNull(nameof(text))] public static StatisticsRange? ParseStatisticsRange(string? text) { - if (text == null) { + if (text is null) { return null; } return text switch { @@ -19,14 +19,12 @@ internal static class EnumHelper { }; } - public static string ToJson(this StatisticsRange range) { - return range switch { - StatisticsRange.AllTime => "all_time", - StatisticsRange.Week => "week", - StatisticsRange.Month => "month", - StatisticsRange.Year => "year", - _ => throw new ArgumentOutOfRangeException(nameof(range), range, "Invalid statistics range specified.") - }; - } + public static string ToJson(this StatisticsRange range) => range switch { + StatisticsRange.AllTime => "all_time", + StatisticsRange.Week => "week", + StatisticsRange.Month => "month", + StatisticsRange.Year => "year", + _ => throw new ArgumentOutOfRangeException(nameof(range), range, "Invalid statistics range specified.") + }; } diff --git a/MetaBrainz.ListenBrainz/Json/Readers/ArtistCountryInfoReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/ArtistCountryInfoReader.cs index 4aac31e..427e7a0 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/ArtistCountryInfoReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/ArtistCountryInfoReader.cs @@ -42,13 +42,13 @@ protected override ArtistCountryInfo ReadObjectContents(ref Utf8JsonReader reade } reader.Read(); } - if (artistCount == null) { + if (artistCount is null) { throw new JsonException("Expected artist count not found or null."); } - if (country == null) { + if (country is null) { throw new JsonException("Expected country code not found or null."); } - if (listenCount == null) { + if (listenCount is null) { throw new JsonException("Expected listen count not found or null."); } return new ArtistCountryInfo(artistCount.Value, country, listenCount.Value) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/ArtistInfoReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/ArtistInfoReader.cs index d62b4d2..af9f135 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/ArtistInfoReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/ArtistInfoReader.cs @@ -46,10 +46,10 @@ protected override ArtistInfo ReadObjectContents(ref Utf8JsonReader reader, Json } reader.Read(); } - if (listenCount == null) { + if (listenCount is null) { throw new JsonException("Expected listen count not found or null."); } - if (name == null) { + if (name is null) { throw new JsonException("Expected artist name not found or null."); } return new ArtistInfo(name, listenCount.Value) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/ErrorInfoReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/ErrorInfoReader.cs index 6e0f00f..7d69230 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/ErrorInfoReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/ErrorInfoReader.cs @@ -38,10 +38,10 @@ protected override ErrorInfo ReadObjectContents(ref Utf8JsonReader reader, JsonS } reader.Read(); } - if (!code.HasValue) { + if (code is null) { throw new JsonException("Expected error code not found or null."); } - if (error == null) { + if (error is null) { throw new JsonException("Expected error message not found or null."); } return new ErrorInfo(code.Value, error) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/FetchedListensReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/FetchedListensReader.cs index 40d8fb3..486784d 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/FetchedListensReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/FetchedListensReader.cs @@ -47,10 +47,10 @@ protected override FetchedListens ReadPayload(ref Utf8JsonReader reader, JsonSer reader.Read(); } listens = PayloadReader.VerifyPayloadContents(count, listens); - if (user == null) { + if (user is null) { throw new JsonException("Expected user id not found or null."); } - if (!ts.HasValue) { + if (ts is null) { throw new JsonException("Expected latest-listen timestamp not found or null."); } return new FetchedListens(listens, ts.Value, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/HourlyActivityReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/HourlyActivityReader.cs index 583d012..38f9e33 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/HourlyActivityReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/HourlyActivityReader.cs @@ -38,13 +38,13 @@ protected override HourlyActivity ReadObjectContents(ref Utf8JsonReader reader, } reader.Read(); } - if (!hour.HasValue) { + if (hour is null) { throw new JsonException("Expected hour not found or null."); } if (hour is < 0 or > 23) { throw new JsonException($"The specified hour ({hour}) is out of range (should be 0-23)."); } - if (!listenCount.HasValue) { + if (listenCount is null) { throw new JsonException("Expected listen count not found or null."); } return new HourlyActivity(hour.Value, listenCount.Value) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/LatestImportReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/LatestImportReader.cs index 69ac785..a6ea038 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/LatestImportReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/LatestImportReader.cs @@ -38,10 +38,10 @@ protected override LatestImport ReadObjectContents(ref Utf8JsonReader reader, Js } reader.Read(); } - if (!ts.HasValue) { + if (ts is null) { throw new JsonException("Expected latest-import timestamp not found or null."); } - if (user == null) { + if (user is null) { throw new JsonException("Expected user id not found or null."); } return new LatestImport(ts.Value, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/ListenCountReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/ListenCountReader.cs index e1f5753..71dfa8c 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/ListenCountReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/ListenCountReader.cs @@ -33,7 +33,7 @@ protected override ListenCount ReadPayload(ref Utf8JsonReader reader, JsonSerial } reader.Read(); } - if (!count.HasValue) { + if (count is null) { throw new JsonException("Expected listen count not found or null."); } return new ListenCount(count.Value) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/ListenReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/ListenReader.cs index 92733cf..6802623 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/ListenReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/ListenReader.cs @@ -51,19 +51,19 @@ protected override Listen ReadObjectContents(ref Utf8JsonReader reader, JsonSeri } reader.Read(); } - if (inserted == null) { + if (inserted is null) { throw new JsonException("Expected inserted-at timestamp not found or null."); } - if (msid == null) { + if (msid is null) { throw new JsonException("Expected MessyBrainz recording id not found or null."); } - if (track == null) { + if (track is null) { throw new JsonException("Expected track metadata not found or null."); } - if (user == null) { + if (user is null) { throw new JsonException("Expected user name not found or null."); } - if (!ts.HasValue) { + if (ts is null) { throw new JsonException("Expected listened-at timestamp not found or null."); } return new Listen(inserted, msid.Value, ts.Value, track, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/ListenTimeRangeReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/ListenTimeRangeReader.cs index de1c551..c2e3b8a 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/ListenTimeRangeReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/ListenTimeRangeReader.cs @@ -50,10 +50,10 @@ protected override ListenTimeRange ReadObjectContents(ref Utf8JsonReader reader, } reader.Read(); } - if (description == null) { + if (description is null) { throw new JsonException("Expected description not found or null."); } - if (listenCount == null) { + if (listenCount is null) { throw new JsonException("Expected listen count not found or null."); } return new ListenTimeRange(description, listenCount.Value) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/MusicBrainzIdMappingsReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/MusicBrainzIdMappingsReader.cs index 77aafe2..fcbfb35 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/MusicBrainzIdMappingsReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/MusicBrainzIdMappingsReader.cs @@ -9,7 +9,7 @@ namespace MetaBrainz.ListenBrainz.Json.Readers; internal sealed class MusicBrainzIdMappingsReader : ObjectReader { - + public static readonly MusicBrainzIdMappingsReader Instance = new(); protected override MusicBrainzIdMappings ReadObjectContents(ref Utf8JsonReader reader, JsonSerializerOptions options) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/PayloadReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/PayloadReader.cs index 2cde71e..9062cbc 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/PayloadReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/PayloadReader.cs @@ -28,7 +28,7 @@ protected sealed override T ReadObjectContents(ref Utf8JsonReader reader, JsonSe protected abstract T ReadPayload(ref Utf8JsonReader reader, JsonSerializerOptions options); protected static IReadOnlyList VerifyPayloadContents(int? count, IReadOnlyList? items) { - if (!count.HasValue) { + if (count is null) { throw new JsonException("Expected payload item count not found or null."); } var n = items?.Count ?? 0; diff --git a/MetaBrainz.ListenBrainz/Json/Readers/PlayingNowReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/PlayingNowReader.cs index d3a7bf6..afe4211 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/PlayingNowReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/PlayingNowReader.cs @@ -61,22 +61,22 @@ protected override PlayingNow ReadPayload(ref Utf8JsonReader reader, JsonSeriali } reader.Read(); } - if (!count.HasValue) { + if (count is null) { throw new JsonException("Expected listen count not found or null."); } if (count > 1) { throw new JsonException($"Too many listens reported (expected at most one; got {count})."); } - if (count == 1 && track == null) { + if (count == 1 && track is null) { throw new JsonException("No listen data found, but the listen count is 1."); } - if (count == 0 && track != null) { + if (count == 0 && track is not null) { throw new JsonException("Listen data found, but the listen count is 0."); } - if (user == null) { + if (user is null) { throw new JsonException("Expected user id not found or null."); } - if (!playingNow.HasValue || playingNow.Value != true) { + if (playingNow is not true) { throw new JsonException("Expected 'playing now' flag not found or set incorrectly."); } return new PlayingNow(track, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/PlayingTrackReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/PlayingTrackReader.cs index a1ae7cf..df0343e 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/PlayingTrackReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/PlayingTrackReader.cs @@ -35,7 +35,7 @@ protected override PlayingTrack ReadObjectContents(ref Utf8JsonReader reader, Js } reader.Read(); } - if (track == null) { + if (track is null) { throw new JsonException("Required track metadata not found."); } return new PlayingTrack(track) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/RecentListensReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/RecentListensReader.cs index 58b0266..c1beb2b 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/RecentListensReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/RecentListensReader.cs @@ -43,7 +43,7 @@ protected override RecentListens ReadPayload(ref Utf8JsonReader reader, JsonSeri reader.Read(); } listens = PayloadReader.VerifyPayloadContents(count, listens); - if (userList == null) { + if (userList is null) { throw new JsonException("Expected user list not found or null."); } return new RecentListens(listens, userList) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/RecordingInfoReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/RecordingInfoReader.cs index da0460a..0b45034 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/RecordingInfoReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/RecordingInfoReader.cs @@ -70,10 +70,10 @@ protected override RecordingInfo ReadObjectContents(ref Utf8JsonReader reader, J } reader.Read(); } - if (listenCount == null) { + if (listenCount is null) { throw new JsonException("Expected listen count not found or null."); } - if (name == null) { + if (name is null) { throw new JsonException("Expected recording name not found or null."); } return new RecordingInfo(name, listenCount.Value) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/ReleaseInfoReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/ReleaseInfoReader.cs index 1d36602..aa81bdd 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/ReleaseInfoReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/ReleaseInfoReader.cs @@ -58,10 +58,10 @@ protected override ReleaseInfo ReadObjectContents(ref Utf8JsonReader reader, Jso } reader.Read(); } - if (listenCount == null) { + if (listenCount is null) { throw new JsonException("Expected listen count not found or null."); } - if (name == null) { + if (name is null) { throw new JsonException("Expected release name not found or null."); } return new ReleaseInfo(name, listenCount.Value) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/SiteArtistStatisticsReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/SiteArtistStatisticsReader.cs index cb905d4..a654180 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/SiteArtistStatisticsReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/SiteArtistStatisticsReader.cs @@ -68,16 +68,16 @@ protected override SiteArtistStatistics ReadPayload(ref Utf8JsonReader reader, J // LB-1013: This ALWAYS reports 1000 as count, so we can't use VerifyPayloadContents(). // artists = this.VerifyPayloadContents(count, artists); artists ??= Array.Empty(); - if (count == null) { + if (count is null) { throw new JsonException("Expected count not found or null."); } - if (lastUpdated == null) { + if (lastUpdated is null) { throw new JsonException("Expected last-updated timestamp not found or null."); } - if (offset == null) { + if (offset is null) { throw new JsonException("Expected offset not found or null."); } - if (range == null) { + if (range is null) { throw new JsonException("Expected range not found or null."); } return new SiteArtistStatistics(count.Value, lastUpdated.Value, offset.Value, range.Value) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/TokenValidationResultReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/TokenValidationResultReader.cs index e28d60b..e7138bc 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/TokenValidationResultReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/TokenValidationResultReader.cs @@ -47,10 +47,10 @@ protected override TokenValidationResult ReadObjectContents(ref Utf8JsonReader r } reader.Read(); } - if (code == null) { + if (code is null) { throw new JsonException("Expected status code not found or null."); } - if (message == null) { + if (message is null) { throw new JsonException("Expected message not found or null."); } return new TokenValidationResult((HttpStatusCode) code.Value, message) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/TrackInfoReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/TrackInfoReader.cs index 4bbc7c7..6a98ee9 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/TrackInfoReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/TrackInfoReader.cs @@ -51,13 +51,13 @@ protected override TrackInfo ReadObjectContents(ref Utf8JsonReader reader, JsonS } reader.Read(); } - if (name == null) { + if (name is null) { throw new JsonException("Expected track name not found or null."); } - if (artist == null) { + if (artist is null) { throw new JsonException("Expected artist name not found or null."); } - if (info == null) { + if (info is null) { throw new JsonException("Expected additional info not found or null."); } return new TrackInfo(name, artist, info) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserArtistMapReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserArtistMapReader.cs index 4876bdb..edf83cd 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserArtistMapReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserArtistMapReader.cs @@ -61,13 +61,13 @@ protected override UserArtistMap ReadPayload(ref Utf8JsonReader reader, JsonSeri } reader.Read(); } - if (lastUpdated == null) { + if (lastUpdated is null) { throw new JsonException("Expected last-updated timestamp not found or null."); } - if (range == null) { + if (range is null) { throw new JsonException("Expected range not found or null."); } - if (user == null) { + if (user is null) { throw new JsonException("Expected user id not found or null."); } return new UserArtistMap(lastUpdated.Value, range.Value, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserArtistStatisticsReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserArtistStatisticsReader.cs index c2b9898..ef50161 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserArtistStatisticsReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserArtistStatisticsReader.cs @@ -74,19 +74,19 @@ protected override UserArtistStatistics ReadPayload(ref Utf8JsonReader reader, J reader.Read(); } artists = PayloadReader.VerifyPayloadContents(count, artists); - if (lastUpdated == null) { + if (lastUpdated is null) { throw new JsonException("Expected last-updated timestamp not found or null."); } - if (offset == null) { + if (offset is null) { throw new JsonException("Expected offset not found or null."); } - if (range == null) { + if (range is null) { throw new JsonException("Expected range not found or null."); } - if (totalCount == null) { + if (totalCount is null) { throw new JsonException("Expected total count not found or null."); } - if (user == null) { + if (user is null) { throw new JsonException("Expected user id not found or null."); } return new UserArtistStatistics(count ?? 0, totalCount.Value, lastUpdated.Value, offset.Value, range.Value, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserDailyActivityReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserDailyActivityReader.cs index f0a44c6..b978141 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserDailyActivityReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserDailyActivityReader.cs @@ -61,13 +61,13 @@ protected override UserDailyActivity ReadPayload(ref Utf8JsonReader reader, Json } reader.Read(); } - if (lastUpdated == null) { + if (lastUpdated is null) { throw new JsonException("Expected last-updated timestamp not found or null."); } - if (range == null) { + if (range is null) { throw new JsonException("Expected range not found or null."); } - if (user == null) { + if (user is null) { throw new JsonException("Expected user id not found or null."); } return new UserDailyActivity(lastUpdated.Value, range.Value, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserListeningActivityReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserListeningActivityReader.cs index d2f28af..5ecf3c9 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserListeningActivityReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserListeningActivityReader.cs @@ -61,13 +61,13 @@ protected override UserListeningActivity ReadPayload(ref Utf8JsonReader reader, } reader.Read(); } - if (lastUpdated == null) { + if (lastUpdated is null) { throw new JsonException("Expected last-updated timestamp not found or null."); } - if (range == null) { + if (range is null) { throw new JsonException("Expected range not found or null."); } - if (user == null) { + if (user is null) { throw new JsonException("Expected user id not found or null."); } return new UserListeningActivity(lastUpdated.Value, range.Value, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserRecordingStatisticsReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserRecordingStatisticsReader.cs index 507ce63..3abc476 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserRecordingStatisticsReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserRecordingStatisticsReader.cs @@ -74,13 +74,13 @@ protected override UserRecordingStatistics ReadPayload(ref Utf8JsonReader reader reader.Read(); } recordings = PayloadReader.VerifyPayloadContents(count, recordings); - if (lastUpdated == null) { + if (lastUpdated is null) { throw new JsonException("Expected last-updated timestamp not found or null."); } - if (range == null) { + if (range is null) { throw new JsonException("Expected range not found or null."); } - if (user == null) { + if (user is null) { throw new JsonException("Expected user id not found or null."); } return new UserRecordingStatistics(lastUpdated.Value, range.Value, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Readers/UserReleaseStatisticsReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/UserReleaseStatisticsReader.cs index c600a69..8961043 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/UserReleaseStatisticsReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/UserReleaseStatisticsReader.cs @@ -74,13 +74,13 @@ protected override UserReleaseStatistics ReadPayload(ref Utf8JsonReader reader, reader.Read(); } releases = PayloadReader.VerifyPayloadContents(count, releases); - if (lastUpdated == null) { + if (lastUpdated is null) { throw new JsonException("Expected last-updated timestamp not found or null."); } - if (range == null) { + if (range is null) { throw new JsonException("Expected range not found or null."); } - if (user == null) { + if (user is null) { throw new JsonException("Expected user id not found or null."); } return new UserReleaseStatistics(lastUpdated.Value, range.Value, user) { diff --git a/MetaBrainz.ListenBrainz/Json/Writers/TrackInfoWriter.cs b/MetaBrainz.ListenBrainz/Json/Writers/TrackInfoWriter.cs index dda252e..421458f 100644 --- a/MetaBrainz.ListenBrainz/Json/Writers/TrackInfoWriter.cs +++ b/MetaBrainz.ListenBrainz/Json/Writers/TrackInfoWriter.cs @@ -14,13 +14,13 @@ protected override void WriteObjectContents(Utf8JsonWriter writer, ISubmittedTra writer.WriteString("track_name", value.Name); { var release = value.Release; - if (release != null) { + if (release is not null) { writer.WriteString("release_name", release); } } { var extra = value.AdditionalInfo; - if (extra != null) { + if (extra is not null) { writer.WritePropertyName("additional_info"); JsonSerializer.Serialize(writer, extra, extra.GetType(), options); } diff --git a/MetaBrainz.ListenBrainz/ListenBrainz.cs b/MetaBrainz.ListenBrainz/ListenBrainz.cs index 23acb8a..eb083cb 100644 --- a/MetaBrainz.ListenBrainz/ListenBrainz.cs +++ b/MetaBrainz.ListenBrainz/ListenBrainz.cs @@ -331,13 +331,13 @@ public Task SetLatestImportAsync(string user, long timestamp, CancellationToken private static IDictionary OptionsForGetStatistics(int? count, int? offset, StatisticsRange? range) { var options = new Dictionary(3); - if (count.HasValue) { + if (count is not null) { options.Add("count", count.Value.ToString(CultureInfo.InvariantCulture)); } - if (offset.HasValue) { + if (offset is not null) { options.Add("offset", offset.Value.ToString(CultureInfo.InvariantCulture)); } - if (range.HasValue) { + if (range is not null) { options.Add("range", range.Value.ToJson()); } return options; @@ -415,7 +415,7 @@ private static IDictionary OptionsForGetStatistics(int? count, i public Task GetArtistMapAsync(string user, StatisticsRange? range = null, bool forceRecalculation = false, CancellationToken cancellationToken = default) { var options = new Dictionary(2); - if (range.HasValue) { + if (range is not null) { options.Add("range", range.Value.ToJson()); } if (forceRecalculation) { @@ -1000,16 +1000,16 @@ public Task GetListenCountAsync(string user, CancellationToken can private static IDictionary OptionsForGetListens(int? count, long? after, long? before, int? timeRange) { var options = new Dictionary(4); - if (count.HasValue) { + if (count is not null) { options.Add("count", count.Value.ToString(CultureInfo.InvariantCulture)); } - if (before.HasValue) { + if (before is not null) { options.Add("max_ts", before.Value.ToString(CultureInfo.InvariantCulture)); } - if (after.HasValue) { + if (after is not null) { options.Add("min_ts", after.Value.ToString(CultureInfo.InvariantCulture)); } - if (timeRange.HasValue) { + if (timeRange is not null) { options.Add("time_range", timeRange.Value.ToString(CultureInfo.InvariantCulture)); } return options; @@ -1457,7 +1457,7 @@ private HttpClient Client { if (this._disposed) { throw new ObjectDisposedException(nameof(ListenBrainz)); } - if (this._client == null) { // Set up the instance with the invariant settings + if (this._client is null) { // Set up the instance with the invariant settings var an = typeof(ListenBrainz).Assembly.GetName(); this._client = new HttpClient { BaseAddress = this.BaseUri, @@ -1548,7 +1548,7 @@ private async Task PerformRequestAsync(string address, Http break; } case "POST": { - if (body != null) { + if (body is not null) { Debug.Print($"[{DateTime.UtcNow}] => BODY: {body}"); } request = new HttpRequestMessage(HttpMethod.Post, requestUri) { @@ -1637,7 +1637,7 @@ private static ProductHeaderValue GetDefaultProductInfo() throw new InvalidOperationException($"Product info must have been set using {nameof(ListenBrainz.DefaultProductInfo)}."); private static string QueryString(IDictionary? options) { - if (options == null || options.Count == 0) { + if (options is null || options.Count == 0) { return ""; } var sb = new StringBuilder(); diff --git a/MetaBrainz.ListenBrainz/Objects/AdditionalInfo.cs b/MetaBrainz.ListenBrainz/Objects/AdditionalInfo.cs index 7b7757a..d368010 100644 --- a/MetaBrainz.ListenBrainz/Objects/AdditionalInfo.cs +++ b/MetaBrainz.ListenBrainz/Objects/AdditionalInfo.cs @@ -47,7 +47,7 @@ public AdditionalInfo(Dictionary fields) { // Extract well-known fields requiring a bit more work { var duration = AdditionalInfo.GetValue(fields, "duration_ms"); - if (duration.HasValue) { + if (duration is not null) { this.Duration = TimeSpan.FromMilliseconds(duration.Value); } } @@ -120,18 +120,18 @@ public AdditionalInfo(Dictionary fields) { #region Helper Methods private static T? GetObject(Dictionary fields, string name) where T : class { - if (fields.TryGetValue(name, out var value) && value != null && value is T typedValue) { + if (fields.TryGetValue(name, out var value) && value is not null && value is T typedValue) { return typedValue; } return null; } private static IReadOnlyList? GetObjectList(Dictionary fields, string name) where T : class { - if (fields.TryGetValue(name, out var value) && value != null) { + if (fields.TryGetValue(name, out var value) && value is not null) { if (value is IReadOnlyList list) { return list; } - if (value is object[] array && array.Length == 0) { + if (value is object[] { Length: 0 }) { return Array.Empty(); } } @@ -139,14 +139,14 @@ public AdditionalInfo(Dictionary fields) { } private static T? GetValue(Dictionary fields, string name) where T : struct { - if (fields.TryGetValue(name, out var value) && value != null && value is T typedValue) { + if (fields.TryGetValue(name, out var value) && value is not null && value is T typedValue) { return typedValue; } return null; } private static IReadOnlyList? GetValueList(Dictionary fields, string name) where T : struct { - if (fields.TryGetValue(name, out var value) && value != null) { + if (fields.TryGetValue(name, out var value) && value is not null) { if (value is IReadOnlyList list) { return list; } @@ -157,7 +157,7 @@ public AdditionalInfo(Dictionary fields) { } return nullableList; } - if (value is object[] array && array.Length == 0) { + if (value is object[] { Length: 0 }) { return Array.Empty(); } } diff --git a/MetaBrainz.ListenBrainz/Objects/MusicBrainzIdMappings.cs b/MetaBrainz.ListenBrainz/Objects/MusicBrainzIdMappings.cs index 85e5ad3..5156eeb 100644 --- a/MetaBrainz.ListenBrainz/Objects/MusicBrainzIdMappings.cs +++ b/MetaBrainz.ListenBrainz/Objects/MusicBrainzIdMappings.cs @@ -10,7 +10,7 @@ namespace MetaBrainz.ListenBrainz.Objects; [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] internal sealed class MusicBrainzIdMappings : JsonBasedObject, IMusicBrainzIdMappings { - + public IReadOnlyList? ArtistIds { get; set; } public Guid? RecordingId { get; set; } diff --git a/MetaBrainz.ListenBrainz/Objects/SubmissionPayload.cs b/MetaBrainz.ListenBrainz/Objects/SubmissionPayload.cs index be0eaa5..5a0bd44 100644 --- a/MetaBrainz.ListenBrainz/Objects/SubmissionPayload.cs +++ b/MetaBrainz.ListenBrainz/Objects/SubmissionPayload.cs @@ -37,6 +37,6 @@ internal sealed class SubmissionPayload : SubmissionPayload where T : ISubmit public SubmissionPayload(string type) : base(type) { } - public List Listens { get; } = new List(); + public List Listens { get; } = new(); } From 458413d767df04eb18ec79879d8c4eac18beae63 Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:19:32 +0100 Subject: [PATCH 08/10] Adjust setters on internal objects In the case of `ErrorInfo`, they were dropped entirely (the constructor sets them); in all other cases they were changed from `set` to `init`. --- .../Json/Readers/PlayingNowReader.cs | 3 ++- MetaBrainz.ListenBrainz/Objects/ArtistInfo.cs | 4 ++-- MetaBrainz.ListenBrainz/Objects/DailyActivity.cs | 14 +++++++------- MetaBrainz.ListenBrainz/Objects/ErrorInfo.cs | 4 ++-- .../Objects/ListenTimeRange.cs | 4 ++-- .../Objects/MusicBrainzIdMappings.cs | 6 +++--- MetaBrainz.ListenBrainz/Objects/PlayingNow.cs | 5 ++--- MetaBrainz.ListenBrainz/Objects/RecordingInfo.cs | 16 ++++++++-------- MetaBrainz.ListenBrainz/Objects/ReleaseInfo.cs | 10 +++++----- .../Objects/SiteArtistStatistics.cs | 2 +- MetaBrainz.ListenBrainz/Objects/Statistics.cs | 4 ++-- .../Objects/TokenValidationResult.cs | 4 ++-- MetaBrainz.ListenBrainz/Objects/TrackInfo.cs | 4 ++-- MetaBrainz.ListenBrainz/Objects/UserArtistMap.cs | 2 +- .../Objects/UserArtistStatistics.cs | 2 +- .../Objects/UserDailyActivity.cs | 2 +- .../Objects/UserListeningActivity.cs | 2 +- .../Objects/UserRecordingStatistics.cs | 6 +++--- .../Objects/UserReleaseStatistics.cs | 6 +++--- 19 files changed, 50 insertions(+), 50 deletions(-) diff --git a/MetaBrainz.ListenBrainz/Json/Readers/PlayingNowReader.cs b/MetaBrainz.ListenBrainz/Json/Readers/PlayingNowReader.cs index afe4211..7ee1f6a 100644 --- a/MetaBrainz.ListenBrainz/Json/Readers/PlayingNowReader.cs +++ b/MetaBrainz.ListenBrainz/Json/Readers/PlayingNowReader.cs @@ -79,7 +79,8 @@ protected override PlayingNow ReadPayload(ref Utf8JsonReader reader, JsonSeriali if (playingNow is not true) { throw new JsonException("Expected 'playing now' flag not found or set incorrectly."); } - return new PlayingNow(track, user) { + return new PlayingNow(user) { + Track = track, UnhandledProperties = rest }; } diff --git a/MetaBrainz.ListenBrainz/Objects/ArtistInfo.cs b/MetaBrainz.ListenBrainz/Objects/ArtistInfo.cs index 03e928a..2546066 100644 --- a/MetaBrainz.ListenBrainz/Objects/ArtistInfo.cs +++ b/MetaBrainz.ListenBrainz/Objects/ArtistInfo.cs @@ -13,11 +13,11 @@ public ArtistInfo(string name, int listenCount) { this.ListenCount = listenCount; } - public IReadOnlyList? Ids { get; set; } + public IReadOnlyList? Ids { get; init; } public int ListenCount { get; } - public Guid? MessyId { get; set; } + public Guid? MessyId { get; init; } public string Name { get; } diff --git a/MetaBrainz.ListenBrainz/Objects/DailyActivity.cs b/MetaBrainz.ListenBrainz/Objects/DailyActivity.cs index cfff8e9..00f24cf 100644 --- a/MetaBrainz.ListenBrainz/Objects/DailyActivity.cs +++ b/MetaBrainz.ListenBrainz/Objects/DailyActivity.cs @@ -7,18 +7,18 @@ namespace MetaBrainz.ListenBrainz.Objects; internal sealed class DailyActivity : JsonBasedObject, IDailyActivity { - public IReadOnlyList? Monday { get; set; } + public IReadOnlyList? Monday { get; init; } - public IReadOnlyList? Tuesday { get; set; } + public IReadOnlyList? Tuesday { get; init; } - public IReadOnlyList? Wednesday { get; set; } + public IReadOnlyList? Wednesday { get; init; } - public IReadOnlyList? Thursday { get; set; } + public IReadOnlyList? Thursday { get; init; } - public IReadOnlyList? Friday { get; set; } + public IReadOnlyList? Friday { get; init; } - public IReadOnlyList? Saturday { get; set; } + public IReadOnlyList? Saturday { get; init; } - public IReadOnlyList? Sunday { get; set; } + public IReadOnlyList? Sunday { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/ErrorInfo.cs b/MetaBrainz.ListenBrainz/Objects/ErrorInfo.cs index 67f3c07..574e232 100644 --- a/MetaBrainz.ListenBrainz/Objects/ErrorInfo.cs +++ b/MetaBrainz.ListenBrainz/Objects/ErrorInfo.cs @@ -12,8 +12,8 @@ public ErrorInfo(int code, string error) { this.Error = error; } - public int Code { get; set; } + public int Code { get; } - public string Error { get; set; } + public string Error { get; } } diff --git a/MetaBrainz.ListenBrainz/Objects/ListenTimeRange.cs b/MetaBrainz.ListenBrainz/Objects/ListenTimeRange.cs index 5210618..d6322d3 100644 --- a/MetaBrainz.ListenBrainz/Objects/ListenTimeRange.cs +++ b/MetaBrainz.ListenBrainz/Objects/ListenTimeRange.cs @@ -16,8 +16,8 @@ public ListenTimeRange(string description, int listenCount) { public int ListenCount { get; } - public DateTimeOffset? RangeEnd { get; set; } + public DateTimeOffset? RangeEnd { get; init; } - public DateTimeOffset? RangeStart { get; set; } + public DateTimeOffset? RangeStart { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/MusicBrainzIdMappings.cs b/MetaBrainz.ListenBrainz/Objects/MusicBrainzIdMappings.cs index 5156eeb..f347225 100644 --- a/MetaBrainz.ListenBrainz/Objects/MusicBrainzIdMappings.cs +++ b/MetaBrainz.ListenBrainz/Objects/MusicBrainzIdMappings.cs @@ -11,10 +11,10 @@ namespace MetaBrainz.ListenBrainz.Objects; [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] internal sealed class MusicBrainzIdMappings : JsonBasedObject, IMusicBrainzIdMappings { - public IReadOnlyList? ArtistIds { get; set; } + public IReadOnlyList? ArtistIds { get; init; } - public Guid? RecordingId { get; set; } + public Guid? RecordingId { get; init; } - public Guid? ReleaseId { get; set; } + public Guid? ReleaseId { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/PlayingNow.cs b/MetaBrainz.ListenBrainz/Objects/PlayingNow.cs index 748ddb2..59806db 100644 --- a/MetaBrainz.ListenBrainz/Objects/PlayingNow.cs +++ b/MetaBrainz.ListenBrainz/Objects/PlayingNow.cs @@ -8,12 +8,11 @@ namespace MetaBrainz.ListenBrainz.Objects; [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] internal sealed class PlayingNow : JsonBasedObject, IPlayingNow { - public PlayingNow(IPlayingTrack? track, string user) { - this.Track = track; + public PlayingNow(string user) { this.User = user; } - public IPlayingTrack? Track { get; } + public IPlayingTrack? Track { get; init; } public string User { get; } diff --git a/MetaBrainz.ListenBrainz/Objects/RecordingInfo.cs b/MetaBrainz.ListenBrainz/Objects/RecordingInfo.cs index 12bf4fa..e70f919 100644 --- a/MetaBrainz.ListenBrainz/Objects/RecordingInfo.cs +++ b/MetaBrainz.ListenBrainz/Objects/RecordingInfo.cs @@ -13,24 +13,24 @@ public RecordingInfo(string name, int listenCount) { this.ListenCount = listenCount; } - public IReadOnlyList? ArtistIds { get; set; } + public IReadOnlyList? ArtistIds { get; init; } - public Guid? ArtistMessyId { get; set; } + public Guid? ArtistMessyId { get; init; } - public string? ArtistName { get; set; } + public string? ArtistName { get; init; } public int ListenCount { get; } - public Guid? Id { get; set; } + public Guid? Id { get; init; } - public Guid? MessyId { get; set; } + public Guid? MessyId { get; init; } public string Name { get; } - public Guid? ReleaseId { get; set; } + public Guid? ReleaseId { get; init; } - public Guid? ReleaseMessyId { get; set; } + public Guid? ReleaseMessyId { get; init; } - public string? ReleaseName { get; set; } + public string? ReleaseName { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/ReleaseInfo.cs b/MetaBrainz.ListenBrainz/Objects/ReleaseInfo.cs index 2090931..b986d94 100644 --- a/MetaBrainz.ListenBrainz/Objects/ReleaseInfo.cs +++ b/MetaBrainz.ListenBrainz/Objects/ReleaseInfo.cs @@ -13,17 +13,17 @@ public ReleaseInfo(string name, int listenCount) { this.ListenCount = listenCount; } - public IReadOnlyList? ArtistIds { get; set; } + public IReadOnlyList? ArtistIds { get; init; } - public Guid? ArtistMessyId { get; set; } + public Guid? ArtistMessyId { get; init; } - public string? ArtistName { get; set; } + public string? ArtistName { get; init; } - public Guid? Id { get; set; } + public Guid? Id { get; init; } public int ListenCount { get; } - public Guid? MessyId { get; set; } + public Guid? MessyId { get; init; } public string Name { get; } diff --git a/MetaBrainz.ListenBrainz/Objects/SiteArtistStatistics.cs b/MetaBrainz.ListenBrainz/Objects/SiteArtistStatistics.cs index e78a08e..336d064 100644 --- a/MetaBrainz.ListenBrainz/Objects/SiteArtistStatistics.cs +++ b/MetaBrainz.ListenBrainz/Objects/SiteArtistStatistics.cs @@ -13,7 +13,7 @@ public SiteArtistStatistics(int count, DateTimeOffset lastUpdated, int offset, S this.Offset = offset; } - public IReadOnlyList? Artists { get; set; } + public IReadOnlyList? Artists { get; init; } public int Count { get; } diff --git a/MetaBrainz.ListenBrainz/Objects/Statistics.cs b/MetaBrainz.ListenBrainz/Objects/Statistics.cs index 77b5994..0c35267 100644 --- a/MetaBrainz.ListenBrainz/Objects/Statistics.cs +++ b/MetaBrainz.ListenBrainz/Objects/Statistics.cs @@ -14,9 +14,9 @@ protected Statistics(DateTimeOffset lastUpdated, StatisticsRange range) { public DateTimeOffset LastUpdated { get; } - public DateTimeOffset? NewestListen { get; set; } + public DateTimeOffset? NewestListen { get; init; } - public DateTimeOffset? OldestListen { get; set; } + public DateTimeOffset? OldestListen { get; init; } public StatisticsRange Range { get; } diff --git a/MetaBrainz.ListenBrainz/Objects/TokenValidationResult.cs b/MetaBrainz.ListenBrainz/Objects/TokenValidationResult.cs index c730226..25ba801 100644 --- a/MetaBrainz.ListenBrainz/Objects/TokenValidationResult.cs +++ b/MetaBrainz.ListenBrainz/Objects/TokenValidationResult.cs @@ -16,8 +16,8 @@ public TokenValidationResult(HttpStatusCode code, string message) { public string Message { get; } - public string? User { get; set; } + public string? User { get; init; } - public bool? Valid { get; set; } + public bool? Valid { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/TrackInfo.cs b/MetaBrainz.ListenBrainz/Objects/TrackInfo.cs index 42ebfa4..3c84709 100644 --- a/MetaBrainz.ListenBrainz/Objects/TrackInfo.cs +++ b/MetaBrainz.ListenBrainz/Objects/TrackInfo.cs @@ -18,10 +18,10 @@ public TrackInfo(string name, string artist, IAdditionalInfo info) { public string Artist { get; } - public IMusicBrainzIdMappings? MusicBrainzIdMappings { get; set; } + public IMusicBrainzIdMappings? MusicBrainzIdMappings { get; init; } public string Name { get; } - public string? Release { get; set; } + public string? Release { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/UserArtistMap.cs b/MetaBrainz.ListenBrainz/Objects/UserArtistMap.cs index fbdde30..f085b8f 100644 --- a/MetaBrainz.ListenBrainz/Objects/UserArtistMap.cs +++ b/MetaBrainz.ListenBrainz/Objects/UserArtistMap.cs @@ -11,6 +11,6 @@ public UserArtistMap(DateTimeOffset lastUpdated, StatisticsRange range, string u : base(lastUpdated, range, user) { } - public IReadOnlyList? Countries { get; set; } + public IReadOnlyList? Countries { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/UserArtistStatistics.cs b/MetaBrainz.ListenBrainz/Objects/UserArtistStatistics.cs index c1c2562..0db0647 100644 --- a/MetaBrainz.ListenBrainz/Objects/UserArtistStatistics.cs +++ b/MetaBrainz.ListenBrainz/Objects/UserArtistStatistics.cs @@ -15,7 +15,7 @@ public UserArtistStatistics(int count, int totalCount, DateTimeOffset lastUpdate this.TotalCount = totalCount; } - public IReadOnlyList? Artists { get; set; } + public IReadOnlyList? Artists { get; init; } public int Count { get; } diff --git a/MetaBrainz.ListenBrainz/Objects/UserDailyActivity.cs b/MetaBrainz.ListenBrainz/Objects/UserDailyActivity.cs index 42d502b..98f565f 100644 --- a/MetaBrainz.ListenBrainz/Objects/UserDailyActivity.cs +++ b/MetaBrainz.ListenBrainz/Objects/UserDailyActivity.cs @@ -10,6 +10,6 @@ public UserDailyActivity(DateTimeOffset lastUpdated, StatisticsRange range, stri : base(lastUpdated, range, user) { } - public IDailyActivity? Activity { get; set; } + public IDailyActivity? Activity { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/UserListeningActivity.cs b/MetaBrainz.ListenBrainz/Objects/UserListeningActivity.cs index 8d65f97..8da1093 100644 --- a/MetaBrainz.ListenBrainz/Objects/UserListeningActivity.cs +++ b/MetaBrainz.ListenBrainz/Objects/UserListeningActivity.cs @@ -11,6 +11,6 @@ public UserListeningActivity(DateTimeOffset lastUpdated, StatisticsRange range, : base(lastUpdated, range, user) { } - public IReadOnlyList? Activity { get; set; } + public IReadOnlyList? Activity { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/UserRecordingStatistics.cs b/MetaBrainz.ListenBrainz/Objects/UserRecordingStatistics.cs index a8dfa32..6e9db2d 100644 --- a/MetaBrainz.ListenBrainz/Objects/UserRecordingStatistics.cs +++ b/MetaBrainz.ListenBrainz/Objects/UserRecordingStatistics.cs @@ -11,10 +11,10 @@ public UserRecordingStatistics(DateTimeOffset lastUpdated, StatisticsRange range : base(lastUpdated, range, user) { } - public IReadOnlyList? Recordings { get; set; } + public IReadOnlyList? Recordings { get; init; } - public int? Offset { get; set; } + public int? Offset { get; init; } - public int? TotalCount { get; set; } + public int? TotalCount { get; init; } } diff --git a/MetaBrainz.ListenBrainz/Objects/UserReleaseStatistics.cs b/MetaBrainz.ListenBrainz/Objects/UserReleaseStatistics.cs index 67e55f0..a553c70 100644 --- a/MetaBrainz.ListenBrainz/Objects/UserReleaseStatistics.cs +++ b/MetaBrainz.ListenBrainz/Objects/UserReleaseStatistics.cs @@ -11,10 +11,10 @@ public UserReleaseStatistics(DateTimeOffset lastUpdated, StatisticsRange range, : base(lastUpdated, range, user) { } - public IReadOnlyList? Releases { get; set; } + public IReadOnlyList? Releases { get; init; } - public int? Offset { get; set; } + public int? Offset { get; init; } - public int? TotalCount { get; set; } + public int? TotalCount { get; init; } } From b37e04ce5d29485d9f3c57f30df75b9bcaf03268 Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:21:18 +0100 Subject: [PATCH 09/10] Move code around In particular, the region with `IDisposable` handling has been moved out of the `Internals` region (it includes public API so is not internal). --- MetaBrainz.ListenBrainz/ListenBrainz.cs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/MetaBrainz.ListenBrainz/ListenBrainz.cs b/MetaBrainz.ListenBrainz/ListenBrainz.cs index eb083cb..b183abb 100644 --- a/MetaBrainz.ListenBrainz/ListenBrainz.cs +++ b/MetaBrainz.ListenBrainz/ListenBrainz.cs @@ -1430,16 +1430,6 @@ public Task ValidateTokenAsync(string token, Cancellatio #endregion - #region Internals - - #region JSON Options - - private static readonly JsonSerializerOptions JsonReaderOptions = JsonUtils.CreateReaderOptions(Converters.Readers); - - private static readonly JsonSerializerOptions JsonWriterOptions = JsonUtils.CreateWriterOptions(Converters.Writers); - - #endregion - #region HTTP Client / IDisposable private AuthenticationHeaderValue? _authentication; @@ -1457,7 +1447,8 @@ private HttpClient Client { if (this._disposed) { throw new ObjectDisposedException(nameof(ListenBrainz)); } - if (this._client is null) { // Set up the instance with the invariant settings + if (this._client is null) { + // Set up the instance with the invariant settings var an = typeof(ListenBrainz).Assembly.GetName(); this._client = new HttpClient { BaseAddress = this.BaseUri, @@ -1508,6 +1499,16 @@ private void Dispose(bool disposing) { #endregion + #region Internals + + #region JSON Options + + private static readonly JsonSerializerOptions JsonReaderOptions = JsonUtils.CreateReaderOptions(Converters.Readers); + + private static readonly JsonSerializerOptions JsonWriterOptions = JsonUtils.CreateWriterOptions(Converters.Writers); + + #endregion + #region Basic Request Execution private async Task GetAsync(string address, IDictionary? options, From bf19e3081b08562ec94e2851b08802306001e018 Mon Sep 17 00:00:00 2001 From: Tim Van Holder Date: Tue, 19 Dec 2023 15:29:12 +0100 Subject: [PATCH 10/10] Add exception documentation All methods that issue web request are now properly documented as potentially throwing `HttpRequestException` or `HttpError`. --- MetaBrainz.ListenBrainz/ListenBrainz.cs | 136 ++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/MetaBrainz.ListenBrainz/ListenBrainz.cs b/MetaBrainz.ListenBrainz/ListenBrainz.cs index b183abb..35bec96 100644 --- a/MetaBrainz.ListenBrainz/ListenBrainz.cs +++ b/MetaBrainz.ListenBrainz/ListenBrainz.cs @@ -266,6 +266,8 @@ public string? UserToken { /// The MusicBrainz ID of the user whose data is requested. /// An object providing the user's ID and latest import timestamp. /// This will access the GET /1/latest-import endpoint. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public ILatestImport GetLatestImport(string user) => AsyncUtils.ResultOf(this.GetLatestImportAsync(user)); /// Get the timestamp of the newest listen submitted by a user in previous imports to ListenBrainz. @@ -273,6 +275,8 @@ public string? UserToken { /// The cancellation token to cancel the operation. /// An object providing the user's ID and latest import timestamp. /// This will access the GET /1/latest-import endpoint. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetLatestImportAsync(string user, CancellationToken cancellationToken = default) => this.GetAsync("latest-import", ListenBrainz.OptionsForLatestImport(user), cancellationToken); @@ -283,6 +287,8 @@ public Task GetLatestImportAsync(string user, CancellationToken c /// This will access the POST /1/latest-import endpoint and requires to be set to the token /// for . /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void SetLatestImport(string user, DateTimeOffset timestamp) => AsyncUtils.ResultOf(this.SetLatestImportAsync(user, timestamp.ToUnixTimeSeconds())); @@ -295,6 +301,8 @@ public void SetLatestImport(string user, DateTimeOffset timestamp) /// This will access the POST /1/latest-import endpoint and requires to be set to the token /// for . /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void SetLatestImport(string user, long timestamp) => AsyncUtils.ResultOf(this.SetLatestImportAsync(user, timestamp)); /// Set the timestamp of the newest listen submitted by a user in previous imports to ListenBrainz. @@ -307,6 +315,8 @@ public void SetLatestImport(string user, DateTimeOffset timestamp) /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task SetLatestImportAsync(string user, DateTimeOffset timestamp, CancellationToken cancellationToken = default) => this.SetLatestImportAsync(user, timestamp.ToUnixTimeSeconds(), cancellationToken); @@ -322,6 +332,8 @@ public Task SetLatestImportAsync(string user, DateTimeOffset timestamp, Cancella /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task SetLatestImportAsync(string user, long timestamp, CancellationToken cancellationToken = default) => this.PostAsync("latest-import", $"{{ ts: {timestamp} }}", ListenBrainz.OptionsForLatestImport(user), cancellationToken); @@ -363,6 +375,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// recorded listen. Otherwise, information is returned about both the current and the previous range. /// /// The requested artist statistics. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public ISiteArtistStatistics? GetArtistStatistics(int? count = null, int? offset = null, StatisticsRange? range = null) => AsyncUtils.ResultOf(this.GetArtistStatisticsAsync(count, offset, range)); @@ -383,6 +397,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// /// The cancellation token to cancel the operation. /// The requested artist statistics. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetArtistStatisticsAsync(int? count = null, int? offset = null, StatisticsRange? range = null, CancellationToken cancellationToken = default) { @@ -403,6 +419,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// The range of data to include in the statistics. /// Indicates whether recalculation of the data should be requested. /// The requested information, or if it has not yet been computed for the user. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IUserArtistMap? GetArtistMap(string user, StatisticsRange? range = null, bool forceRecalculation = false) => AsyncUtils.ResultOf(this.GetArtistMapAsync(user, range, forceRecalculation)); @@ -412,6 +430,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// Indicates whether recalculation of the data should be requested. /// The cancellation token to cancel the operation. /// The requested information, or if it has not yet been computed for the user. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetArtistMapAsync(string user, StatisticsRange? range = null, bool forceRecalculation = false, CancellationToken cancellationToken = default) { var options = new Dictionary(2); @@ -442,6 +462,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// /// The requested artist statistics, or if statistics have not yet been computed for the user. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IUserArtistStatistics? GetArtistStatistics(string user, int? count = null, int? offset = null, StatisticsRange? range = null) => AsyncUtils.ResultOf(this.GetArtistStatisticsAsync(user, count, offset, range)); @@ -461,6 +483,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// /// The requested artist statistics, or if statistics have not yet been computed for the user. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetArtistStatisticsAsync(string user, int? count = null, int? offset = null, StatisticsRange? range = null, CancellationToken cancellationToken = default) { @@ -477,6 +501,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// The user for whom the information is requested. /// The range of data to include in the information. /// The requested daily activity. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IUserDailyActivity? GetDailyActivity(string user, StatisticsRange? range = null) => AsyncUtils.ResultOf(this.GetDailyActivityAsync(user, range)); @@ -485,6 +511,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// The range of data to include in the information. /// The cancellation token to cancel the operation. /// The requested daily activity. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetDailyActivityAsync(string user, StatisticsRange? range = null, CancellationToken cancellationToken = default) { var address = $"stats/user/{user}/daily-activity"; @@ -504,6 +532,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// recorded listen. Otherwise, information is returned about both the current and the previous range. /// /// The requested listening activity. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IUserListeningActivity? GetListeningActivity(string user, StatisticsRange? range = null) => AsyncUtils.ResultOf(this.GetListeningActivityAsync(user, range)); @@ -516,6 +546,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// /// The cancellation token to cancel the operation. /// The requested listening activity. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetListeningActivityAsync(string user, StatisticsRange? range = null, CancellationToken cancellationToken = default) { var address = $"stats/user/{user}/listening-activity"; @@ -541,6 +573,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// /// The requested recording statistics, or if statistics have not yet been computed for the user. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IUserRecordingStatistics? GetRecordingStatistics(string user, int? count = null, int? offset = null, StatisticsRange? range = null) => AsyncUtils.ResultOf(this.GetRecordingStatisticsAsync(user, count, offset, range)); @@ -560,6 +594,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// /// The requested recording statistics, or if statistics have not yet been computed for the user. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetRecordingStatisticsAsync(string user, int? count = null, int? offset = null, StatisticsRange? range = null, CancellationToken cancellationToken = default) { @@ -586,6 +622,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// /// The requested release statistics, or if statistics have not yet been computed for the user. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IUserReleaseStatistics? GetReleaseStatistics(string user, int? count = null, int? offset = null, StatisticsRange? range = null) => AsyncUtils.ResultOf(this.GetReleaseStatisticsAsync(user, count, offset, range)); @@ -605,6 +643,8 @@ private static IDictionary OptionsForGetStatistics(int? count, i /// /// The requested releases statistics, or if statistics have not yet been computed for the user. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetReleaseStatisticsAsync(string user, int? count = null, int? offset = null, StatisticsRange? range = null, CancellationToken cancellationToken = default) { @@ -657,6 +697,8 @@ private Task SubmitListensAsync(string payload, CancellationToken cancellationTo /// , this will split them up and submit them in chunks to avoid hitting that limit. As such, one /// call to this method may result in multiple web service requests, which may affect rate limiting. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void ImportListens(IEnumerable listens) => AsyncUtils.ResultOf(this.ImportListensAsync(listens)); /// Imports a set of listens for the user whose token is set in . @@ -669,6 +711,8 @@ private Task SubmitListensAsync(string payload, CancellationToken cancellationTo /// , this will split them up and submit them in chunks to avoid hitting that limit. As such, one /// call to this method may result in multiple web service requests, which may affect rate limiting. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void ImportListens(params ISubmittedListen[] listens) => AsyncUtils.ResultOf(this.ImportListensAsync(listens)); /// Imports a set of listens for the user whose token is set in . @@ -683,6 +727,8 @@ private Task SubmitListensAsync(string payload, CancellationToken cancellationTo /// , this will split them up and submit them in chunks to avoid hitting that limit. As such, one /// call to this method may result in multiple web service requests, which may affect rate limiting. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public async Task ImportListensAsync(IAsyncEnumerable listens, CancellationToken cancellationToken = default) { var payload = SubmissionPayload.CreateImport(); await foreach (var listen in listens.ConfigureAwait(false).WithCancellation(cancellationToken)) { @@ -711,6 +757,8 @@ public async Task ImportListensAsync(IAsyncEnumerable listens, /// , this will split them up and submit them in chunks to avoid hitting that limit. As such, one /// call to this method may result in multiple web service requests, which may affect rate limiting. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public async Task ImportListensAsync(IEnumerable listens, CancellationToken cancellationToken = default) { var payload = SubmissionPayload.CreateImport(); foreach (var listen in listens) { @@ -745,6 +793,8 @@ private async Task ImportListensAsync(IEnumerable serializedListens, Can /// , this will split them up and submit them in chunks to avoid hitting that limit. As such, one /// call to this method may result in multiple web service requests, which may affect rate limiting. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task ImportListensAsync(CancellationToken cancellationToken, params ISubmittedListen[] listens) => this.ImportListensAsync(listens, cancellationToken); @@ -759,6 +809,8 @@ public Task ImportListensAsync(CancellationToken cancellationToken, params ISubm /// , this will split them up and submit them in chunks to avoid hitting that limit. As such, one /// call to this method may result in multiple web service requests, which may affect rate limiting. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task ImportListensAsync(params ISubmittedListen[] listens) => this.ImportListensAsync((IEnumerable) listens); @@ -799,6 +851,8 @@ private IEnumerable SerializeImport(SubmissionPayload /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void SetNowPlaying(ISubmittedListenData listen) => AsyncUtils.ResultOf(this.SetNowPlayingAsync(listen)); /// Sets the "now playing" information for the user whose token is set in . @@ -810,6 +864,8 @@ private IEnumerable SerializeImport(SubmissionPayload /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void SetNowPlaying(string track, string artist, string? release = null) => AsyncUtils.ResultOf(this.SetNowPlayingAsync(track, artist, release)); @@ -822,6 +878,8 @@ public void SetNowPlaying(string track, string artist, string? release = null) /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task SetNowPlayingAsync(ISubmittedListenData listen, CancellationToken cancellationToken = default) => this.SubmitListensAsync(SubmissionPayload.CreatePlayingNow(listen), cancellationToken); @@ -836,6 +894,8 @@ public Task SetNowPlayingAsync(ISubmittedListenData listen, CancellationToken ca /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task SetNowPlayingAsync(string track, string artist, string? release = null, CancellationToken cancellationToken = default) => this.SetNowPlayingAsync(new SubmittedListenData(track, artist, release), cancellationToken); @@ -852,6 +912,8 @@ public Task SetNowPlayingAsync(string track, string artist, string? release = nu /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void SubmitSingleListen(ISubmittedListen listen) => AsyncUtils.ResultOf(this.SubmitSingleListenAsync(listen)); /// @@ -866,6 +928,8 @@ public Task SetNowPlayingAsync(string track, string artist, string? release = nu /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void SubmitSingleListen(DateTimeOffset timestamp, string track, string artist, string? release = null) => AsyncUtils.ResultOf(this.SubmitSingleListenAsync(timestamp, track, artist, release)); @@ -884,6 +948,8 @@ public void SubmitSingleListen(DateTimeOffset timestamp, string track, string ar /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void SubmitSingleListen(long timestamp, string track, string artist, string? release = null) => AsyncUtils.ResultOf(this.SubmitSingleListenAsync(timestamp, track, artist, release)); @@ -899,6 +965,8 @@ public void SubmitSingleListen(long timestamp, string track, string artist, stri /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public void SubmitSingleListen(string track, string artist, string? release = null) => AsyncUtils.ResultOf(this.SubmitSingleListenAsync(track, artist, release)); @@ -913,6 +981,8 @@ public void SubmitSingleListen(string track, string artist, string? release = nu /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task SubmitSingleListenAsync(ISubmittedListen listen, CancellationToken cancellationToken = default) => this.SubmitListensAsync(SubmissionPayload.CreateSingle(listen), cancellationToken); @@ -930,6 +1000,8 @@ public Task SubmitSingleListenAsync(ISubmittedListen listen, CancellationToken c /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task SubmitSingleListenAsync(DateTimeOffset timestamp, string track, string artist, string? release = null, CancellationToken cancellationToken = default) => this.SubmitSingleListenAsync(new SubmittedListen(timestamp, track, artist, release), cancellationToken); @@ -951,6 +1023,8 @@ public Task SubmitSingleListenAsync(DateTimeOffset timestamp, string track, stri /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task SubmitSingleListenAsync(long timestamp, string track, string artist, string? release = null, CancellationToken cancellationToken = default) => this.SubmitSingleListenAsync(new SubmittedListen(timestamp, track, artist, release), cancellationToken); @@ -968,6 +1042,8 @@ public Task SubmitSingleListenAsync(long timestamp, string track, string artist, /// Users can find their token on their profile page: /// https://listenbrainz.org/profile/. /// + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task SubmitSingleListenAsync(string track, string artist, string? release = null, CancellationToken cancellationToken = default) => this.SubmitSingleListenAsync(new SubmittedListen(track, artist, release), cancellationToken); @@ -982,6 +1058,8 @@ public Task SubmitSingleListenAsync(string track, string artist, string? release /// The MusicBrainz ID of the user whose listen count is requested. /// An object providing the number of listens submitted by . /// This will access the GET /1/user/USER/listen-count endpoint. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IListenCount GetListenCount(string user) => AsyncUtils.ResultOf(this.GetListenCountAsync(user)); /// Gets the number of listens submitted to ListenBrainz by a particular user. @@ -989,6 +1067,8 @@ public Task SubmitSingleListenAsync(string track, string artist, string? release /// The cancellation token to cancel the operation. /// An object providing the number of listens submitted by . /// This will access the GET /1/user/USER/listen-count endpoint. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetListenCountAsync(string user, CancellationToken cancellationToken = default) => this.GetAsync($"user/{user}/listen-count", null, cancellationToken); @@ -1039,6 +1119,8 @@ private Task PerformGetListensAsync(string user, long? after, l /// If not specified, will be used as time range. /// /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IFetchedListens GetListens(string user, int? count = null, int? timeRange = null) => this.PerformGetListens(user, null, null, count, timeRange); @@ -1054,6 +1136,8 @@ public IFetchedListens GetListens(string user, int? count = null, int? timeRange /// /// The cancellation token to cancel the operation. /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetListensAsync(string user, int? count = null, int? timeRange = null, CancellationToken cancellationToken = default) => this.PerformGetListensAsync(user, null, null, count, timeRange, cancellationToken); @@ -1078,6 +1162,8 @@ public Task GetListensAsync(string user, int? count = null, int /// If not specified, will be used as time range. /// /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IFetchedListens GetListensAfter(string user, long after, int? count = null, int? timeRange = null) => this.PerformGetListens(user, after, null, count, timeRange); @@ -1096,6 +1182,8 @@ public IFetchedListens GetListensAfter(string user, long after, int? count = nul /// If not specified, will be used as time range. /// /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IFetchedListens GetListensAfter(string user, DateTimeOffset after, int? count = null, int? timeRange = null) => this.PerformGetListens(user, after.ToUnixTimeSeconds(), null, count, timeRange); @@ -1116,6 +1204,8 @@ public IFetchedListens GetListensAfter(string user, DateTimeOffset after, int? c /// /// The cancellation token to cancel the operation. /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetListensAfterAsync(string user, long after, int? count = null, int? timeRange = null, CancellationToken cancellationToken = default) => this.PerformGetListensAsync(user, after, null, count, timeRange, cancellationToken); @@ -1136,6 +1226,8 @@ public Task GetListensAfterAsync(string user, long after, int? /// /// The cancellation token to cancel the operation. /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetListensAfterAsync(string user, DateTimeOffset after, int? count = null, int? timeRange = null, CancellationToken cancellationToken = default) => this.PerformGetListensAsync(user, after.ToUnixTimeSeconds(), null, count, timeRange, cancellationToken); @@ -1160,6 +1252,8 @@ public Task GetListensAfterAsync(string user, DateTimeOffset af /// If not specified, will be used as time range. /// /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IFetchedListens GetListensBefore(string user, long before, int? count = null, int? timeRange = null) => this.PerformGetListens(user, null, before, count, timeRange); @@ -1178,6 +1272,8 @@ public IFetchedListens GetListensBefore(string user, long before, int? count = n /// If not specified, will be used as time range. /// /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IFetchedListens GetListensBefore(string user, DateTimeOffset before, int? count = null, int? timeRange = null) => this.PerformGetListens(user, null, before.ToUnixTimeSeconds(), count, timeRange); @@ -1198,6 +1294,8 @@ public IFetchedListens GetListensBefore(string user, DateTimeOffset before, int? /// /// The cancellation token to cancel the operation. /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetListensBeforeAsync(string user, long before, int? count = null, int? timeRange = null, CancellationToken cancellationToken = default) => this.PerformGetListensAsync(user, null, before, count, timeRange, cancellationToken); @@ -1218,6 +1316,8 @@ public Task GetListensBeforeAsync(string user, long before, int /// /// The cancellation token to cancel the operation. /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetListensBeforeAsync(string user, DateTimeOffset before, int? count = null, int? timeRange = null, CancellationToken cancellationToken = default) => this.PerformGetListensAsync(user, null, before.ToUnixTimeSeconds(), count, timeRange, cancellationToken); @@ -1243,6 +1343,8 @@ public Task GetListensBeforeAsync(string user, DateTimeOffset b /// If not specified, this will return up to listens. /// /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IFetchedListens GetListensBetween(string user, long after, long before, int? count = null) => this.PerformGetListens(user, after, before, count); @@ -1261,6 +1363,8 @@ public IFetchedListens GetListensBetween(string user, long after, long before, i /// If not specified, this will return up to listens. /// /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IFetchedListens GetListensBetween(string user, DateTimeOffset after, DateTimeOffset before, int? count = null) => this.PerformGetListens(user, after.ToUnixTimeSeconds(), before.ToUnixTimeSeconds(), count); @@ -1282,6 +1386,8 @@ public IFetchedListens GetListensBetween(string user, DateTimeOffset after, Date /// /// The cancellation token to cancel the operation. /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetListensBetweenAsync(string user, long after, long before, int? count = null, CancellationToken cancellationToken = default) => this.PerformGetListensAsync(user, after, before, count, null, cancellationToken); @@ -1302,6 +1408,8 @@ public Task GetListensBetweenAsync(string user, long after, lon /// /// The cancellation token to cancel the operation. /// The requested listens, in descending timestamp order. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetListensBetweenAsync(string user, DateTimeOffset after, DateTimeOffset before, int? count = null, CancellationToken cancellationToken = default) => this.PerformGetListensAsync(user, after.ToUnixTimeSeconds(), before.ToUnixTimeSeconds(), count, null, cancellationToken); @@ -1315,12 +1423,16 @@ public Task GetListensBetweenAsync(string user, DateTimeOffset /// Gets a user's currently-playing listen(s). /// The MusicBrainz ID of the user whose data is needed. /// The requested listens (typically 0 or 1). + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IPlayingNow GetPlayingNow(string user) => AsyncUtils.ResultOf(this.GetPlayingNowAsync(user)); /// Gets a user's currently-playing listen(s). /// The MusicBrainz ID of the user whose data is needed. /// The cancellation token to cancel the operation. /// The requested listens (typically 0 or 1). + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetPlayingNowAsync(string user, CancellationToken cancellationToken = default) => this.GetAsync($"user/{user}/playing-now", null, cancellationToken); @@ -1336,17 +1448,23 @@ private static Dictionary OptionsForRecentListens(int limit) /// Gets recent listen(s) for a set of users. /// The MusicBrainz IDs of the users whose data is needed. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IRecentListens GetRecentListens(IEnumerable users) => AsyncUtils.ResultOf(this.GetRecentListensAsync(users)); /// Gets recent listen(s) for a set of users. /// The MusicBrainz IDs of the users whose data is needed. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IRecentListens GetRecentListens(params string[] users) => AsyncUtils.ResultOf(this.GetRecentListensAsync(users)); /// Gets recent listen(s) for a set of users. /// The maximum number of listens to return. /// The MusicBrainz IDs of the users whose data is needed. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IRecentListens GetRecentListens(int limit, IEnumerable users) => AsyncUtils.ResultOf(this.GetRecentListensAsync(limit, users)); @@ -1354,6 +1472,8 @@ public IRecentListens GetRecentListens(int limit, IEnumerable users) /// The maximum number of listens to return. /// The MusicBrainz IDs of the users whose data is needed. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public IRecentListens GetRecentListens(int limit, params string[] users) => AsyncUtils.ResultOf(this.GetRecentListensAsync(limit, users)); @@ -1361,6 +1481,8 @@ public IRecentListens GetRecentListens(int limit, params string[] users) /// The cancellation token to cancel the operation. /// The MusicBrainz IDs of the users whose data is needed. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetRecentListensAsync(CancellationToken cancellationToken, params string[] users) => this.GetRecentListensAsync(users, cancellationToken); @@ -1368,6 +1490,8 @@ public Task GetRecentListensAsync(CancellationToken cancellation /// The cancellation token to cancel the operation. /// The MusicBrainz IDs of the users whose data is needed. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetRecentListensAsync(IEnumerable users, CancellationToken cancellationToken = default) { var address = $"users/{ListenBrainz.FormatUserList(users)}/recent-listens"; return this.GetAsync(address, null, cancellationToken); @@ -1378,6 +1502,8 @@ public Task GetRecentListensAsync(IEnumerable users, Can /// The cancellation token to cancel the operation. /// The MusicBrainz IDs of the users whose data is needed. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetRecentListensAsync(int limit, CancellationToken cancellationToken, params string[] users) => this.GetRecentListensAsync(limit, users, cancellationToken); @@ -1386,6 +1512,8 @@ public Task GetRecentListensAsync(int limit, CancellationToken c /// The MusicBrainz IDs of the users whose data is needed. /// The cancellation token to cancel the operation. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetRecentListensAsync(int limit, IEnumerable users, CancellationToken cancellationToken = default) { var requestUri = $"users/{ListenBrainz.FormatUserList(users)}/recent-listens"; @@ -1397,12 +1525,16 @@ public Task GetRecentListensAsync(int limit, IEnumerable /// The maximum number of listens to return. /// The MusicBrainz IDs of the users whose data is needed. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetRecentListensAsync(int limit, params string[] users) => this.GetRecentListensAsync(limit, (IEnumerable) users); /// Gets recent listen(s) for a set of users. /// The MusicBrainz IDs of the users whose data is needed. /// The requested listens. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task GetRecentListensAsync(params string[] users) => this.GetAsync($"users/{ListenBrainz.FormatUserList(users)}/recent-listens", null); @@ -1415,12 +1547,16 @@ public Task GetRecentListensAsync(params string[] users) /// Validates a given user token. /// The user token to validate. /// The result of the validation. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public ITokenValidationResult ValidateToken(string token) => AsyncUtils.ResultOf(this.ValidateTokenAsync(token)); /// Validates a given user token. /// The user token to validate. /// The cancellation token to cancel the operation. /// The result of the validation. + /// When there was a problem sending the web service request. + /// When the web service sends a response indicating an error. public Task ValidateTokenAsync(string token, CancellationToken cancellationToken = default) { var options = ListenBrainz.OptionsForTokenValidation(token); return this.GetAsync("validate-token", options, cancellationToken);