diff --git a/README.md b/README.md
index 78f3d79..94c2bb1 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Implementation of the [MusicBrainz](https://musicbrainz.org/) API version 2 for
- First class MusicBrainz entities `Artist`, `Label`, `ReleaseGroup`, `Release` and `Recording` supporting asynchronous `Get` (lookup by MBID), `Search` and `Browse`.
- Advanced `Search` using Lucene query syntax (see [search documentation](https://musicbrainz.org/doc/MusicBrainz_API/Search) for supported fields).
-- Support for sub-queries in lookup requests (see MusicBrainz [documentation](https://musicbrainz.org/doc/MusicBrainz_API#inc=_arguments_which_affect_subqueries) and [wiki](https://wiki.musicbrainz.org/User:Nikki/ws/2) for a list of supported `inc` parameters).
+- Support for sub-queries in lookup requests (see MusicBrainz [documentation](https://musicbrainz.org/doc/MusicBrainz_API#Subqueries) and [wiki](https://wiki.musicbrainz.org/User:Nikki/ws/2) for a list of supported `inc` parameters).
- Limited support for entity relationships.
More information about the MusicBrainz API can be found in the [documentation](https://musicbrainz.org/doc/MusicBrainz_API).
diff --git a/assets/readme.md b/assets/readme.md
index 81e19a8..2493c66 100644
--- a/assets/readme.md
+++ b/assets/readme.md
@@ -6,7 +6,7 @@ Implementation of the [MusicBrainz](https://musicbrainz.org/) API version 2 for
- First class MusicBrainz entities `Artist`, `Label`, `ReleaseGroup`, `Release` and `Recording` supporting asynchronous `Get` (lookup by MBID), `Search` and `Browse`.
- Advanced `Search` using Lucene query syntax (see [search documentation](https://musicbrainz.org/doc/MusicBrainz_API/Search) for supported fields).
-- Support for sub-queries in lookup requests (see MusicBrainz [documentation](https://musicbrainz.org/doc/MusicBrainz_API#inc=_arguments_which_affect_subqueries) and [wiki](https://wiki.musicbrainz.org/User:Nikki/ws/2) for a list of supported `inc` parameters).
+- Support for sub-queries in lookup requests (see MusicBrainz [documentation](https://musicbrainz.org/doc/MusicBrainz_API#Subqueries) and [wiki](https://wiki.musicbrainz.org/User:Nikki/ws/2) for a list of supported `inc` parameters).
- Limited support for entity relationships.
More information about the MusicBrainz API can be found [here](https://musicbrainz.org/doc/MusicBrainz_API).
diff --git a/src/Hqub.MusicBrainz.Client/Example5.cs b/src/Hqub.MusicBrainz.Client/Example5.cs
index c6869ca..a92b23d 100644
--- a/src/Hqub.MusicBrainz.Client/Example5.cs
+++ b/src/Hqub.MusicBrainz.Client/Example5.cs
@@ -36,7 +36,7 @@ private static async Task Browse(MusicBrainzClient client, string name)
Console.WriteLine();
// Show official albums.
- foreach (var item in groups.Items.Where(g => IsOfficial(g)).OrderBy(g => g.FirstReleaseDate))
+ foreach (var item in groups.Items.Where(IsOfficial).OrderBy(g => g.FirstReleaseDate))
{
Console.WriteLine(" {0} - {1} {2} {3}", item.FirstReleaseDate.ToShortDate(),
item.Id, GetRating(item.Rating, 10), item.Title);
@@ -47,7 +47,7 @@ private static async Task Browse(MusicBrainzClient client, string name)
Console.WriteLine();
// Show compilations.
- foreach (var item in groups.Items.Where(g => IsCompilation(g)).OrderBy(g => g.FirstReleaseDate))
+ foreach (var item in groups.Items.Where(IsCompilation).OrderBy(g => g.FirstReleaseDate))
{
Console.WriteLine(" {0} - {1} {2} {3}", item.FirstReleaseDate.ToShortDate(),
item.Id, GetRating(item.Rating, 10), item.Title);
diff --git a/src/Hqub.MusicBrainz.Client/Example7.cs b/src/Hqub.MusicBrainz.Client/Example7.cs
index aa3d30d..3e7a1cc 100644
--- a/src/Hqub.MusicBrainz.Client/Example7.cs
+++ b/src/Hqub.MusicBrainz.Client/Example7.cs
@@ -8,7 +8,7 @@ namespace Hqub.MusicBrainz.Client
///
/// Search for a label using client.Labels.SearchAsync() and lookup label details
- /// like releases and related urls using clients.Label.GetAsync().
+ /// like releases and related urls using client.Labels.GetAsync().
///
public class Example7
{
diff --git a/src/Hqub.MusicBrainz.Client/FetchTestData.cs b/src/Hqub.MusicBrainz.Client/FetchTestData.cs
index 031f4ce..f19c570 100644
--- a/src/Hqub.MusicBrainz.Client/FetchTestData.cs
+++ b/src/Hqub.MusicBrainz.Client/FetchTestData.cs
@@ -43,7 +43,6 @@ public static async Task Download(bool overwrite = false)
foreach (var item in Data)
{
await Get(client, item.Key, item.Value, overwrite);
-
}
Console.Write("Done. Press any key to quit.");
diff --git a/src/Hqub.MusicBrainz.Client/Program.cs b/src/Hqub.MusicBrainz.Client/Program.cs
index 5789d5a..c7fa7c1 100644
--- a/src/Hqub.MusicBrainz.Client/Program.cs
+++ b/src/Hqub.MusicBrainz.Client/Program.cs
@@ -14,6 +14,10 @@ private static async Task Main(string[] args)
{
await RunExamples();
}
+ catch (WebServiceException e)
+ {
+ Error(e);
+ }
catch (Exception e)
{
Console.WriteLine(e.Message);
@@ -66,5 +70,23 @@ private static void Header(string title)
Console.ForegroundColor = color;
}
+
+ private static void Error(WebServiceException e)
+ {
+ var color = Console.ForegroundColor;
+
+ Console.ForegroundColor = ConsoleColor.DarkRed;
+
+ Console.WriteLine();
+ Console.WriteLine("MusicBrainz web service reported an error");
+
+ Console.ForegroundColor = color;
+
+ Console.WriteLine(" Requested URL: " + e.Uri);
+ Console.WriteLine(" Status Code: " + e.StatusCode);
+ Console.WriteLine(" Message: " + e.Message);
+ Console.WriteLine();
+
+ }
}
}
diff --git a/src/Hqub.MusicBrainz.Tests/Hqub.MusicBrainz.Tests.csproj b/src/Hqub.MusicBrainz.Tests/Hqub.MusicBrainz.Tests.csproj
index 9cc78dd..0ab5f16 100644
--- a/src/Hqub.MusicBrainz.Tests/Hqub.MusicBrainz.Tests.csproj
+++ b/src/Hqub.MusicBrainz.Tests/Hqub.MusicBrainz.Tests.csproj
@@ -50,8 +50,8 @@
-
-
+
+
diff --git a/src/Hqub.MusicBrainz.Tests/ReleaseGetTests.cs b/src/Hqub.MusicBrainz.Tests/ReleaseGetTests.cs
index 08da7ba..a206427 100644
--- a/src/Hqub.MusicBrainz.Tests/ReleaseGetTests.cs
+++ b/src/Hqub.MusicBrainz.Tests/ReleaseGetTests.cs
@@ -36,6 +36,7 @@ public void TestReleaseElements()
Assert.That(release.Date, Is.EqualTo("2012-06-11"));
Assert.That(release.Country, Is.EqualTo("US"));
Assert.That(release.Barcode, Is.EqualTo("809236126221"));
+ Assert.That(release.Packaging, Is.EqualTo("Cardboard/Paper Sleeve"));
}
[Test]
diff --git a/src/Hqub.MusicBrainz.Tests/UrlBuilderTests.cs b/src/Hqub.MusicBrainz.Tests/UrlBuilderTests.cs
new file mode 100644
index 0000000..99632d1
--- /dev/null
+++ b/src/Hqub.MusicBrainz.Tests/UrlBuilderTests.cs
@@ -0,0 +1,77 @@
+namespace Hqub.MusicBrainz.Tests
+{
+ using NUnit.Framework;
+
+ public class UrlBuilder
+ {
+ private MusicBrainzClient client;
+
+ [OneTimeSetUp]
+ public void Init()
+ {
+ client = new MusicBrainzClient();
+ }
+
+ [Test]
+ public void TestLookupRequest()
+ {
+ var request = client.Releases.Get("00000000-0000-0000-0000-000000000000");
+
+ const string expected = "release/00000000-0000-0000-0000-000000000000?fmt=json";
+
+ Assert.That(request.ToString(), Is.EqualTo(expected));
+
+ request.Include("artists", "tags");
+
+ Assert.That(request.ToString(), Is.EqualTo(expected + "&inc=artists+tags"));
+ }
+
+ [Test]
+ public void TestSearchRequest()
+ {
+ var request = client.Releases.Search("search-string");
+
+ const string expected = "release?query=search-string&fmt=json";
+
+ Assert.That(request.ToString(), Is.EqualTo(expected));
+
+ request.Limit(10);
+
+ Assert.That(request.ToString(), Is.EqualTo(expected + "&limit=10"));
+
+ request.Offset(20);
+
+ Assert.That(request.ToString(), Is.EqualTo(expected + "&limit=10&offset=20"));
+ }
+
+ [Test]
+ public void TestBrowseRequest()
+ {
+ var request = client.Releases.Browse("label", "00000000-0000-0000-0000-000000000000");
+
+ const string expected = "release?label=00000000-0000-0000-0000-000000000000&fmt=json";
+
+ Assert.That(request.ToString(), Is.EqualTo(expected));
+
+ request.Limit(10);
+
+ Assert.That(request.ToString(), Is.EqualTo(expected + "&limit=10"));
+
+ request.Offset(20);
+
+ Assert.That(request.ToString(), Is.EqualTo(expected + "&limit=10&offset=20"));
+
+ request.Include("artist-credits", "tags");
+
+ Assert.That(request.ToString(), Is.EqualTo(expected + "&limit=10&offset=20&inc=artist-credits+tags"));
+
+ request.Type("album|ep");
+
+ Assert.That(request.ToString(), Is.EqualTo(expected + "&limit=10&offset=20&inc=artist-credits+tags&type=album|ep"));
+
+ request.Status("official");
+
+ Assert.That(request.ToString(), Is.EqualTo(expected + "&limit=10&offset=20&inc=artist-credits+tags&type=album|ep&status=official"));
+ }
+ }
+}
diff --git a/src/Hqub.MusicBrainz/Entities/Release.cs b/src/Hqub.MusicBrainz/Entities/Release.cs
index f24b200..754bdde 100644
--- a/src/Hqub.MusicBrainz/Entities/Release.cs
+++ b/src/Hqub.MusicBrainz/Entities/Release.cs
@@ -68,6 +68,12 @@ public class Release : IEntity
[DataMember(Name = "barcode")]
public string Barcode { get; set; }
+ ///
+ /// Gets or sets the packaging.
+ ///
+ [DataMember(Name = "packaging")]
+ public string Packaging { get; set; }
+
///
/// Gets or sets the release-group.
///
diff --git a/src/Hqub.MusicBrainz/Hqub.MusicBrainz.csproj b/src/Hqub.MusicBrainz/Hqub.MusicBrainz.csproj
index 80d06eb..5fdc96e 100644
--- a/src/Hqub.MusicBrainz/Hqub.MusicBrainz.csproj
+++ b/src/Hqub.MusicBrainz/Hqub.MusicBrainz.csproj
@@ -13,10 +13,10 @@
Boris Glebov, Christian Woltering
https://github.com/avatar29A/MusicBrainz
https://github.com/avatar29A/MusicBrainz
- 3.2.0.0
- 3.2.0.0
+ 4.0.0.0
+ 4.0.0.0
music musicbrainz brainz
- 3.2.0
+ 4.0.0
Hqub.MusicBrainz
Hqub.MusicBrainz
Debug;Release
@@ -27,23 +27,18 @@
MIT
git
- Hqub.MusicBrainz version 3 breaking changes:
+ Hqub.MusicBrainz version 4.0 breaking changes:
- * Remove the obsolete static API: fix by using the MusicBrainzClient class.
- * Remove the ".API" suffix from assembly and namespaces: fix by removing the ".API" suffix in your using statements.
+ * Consistently return QueryResult instead of specialized collection types from search and browse requests.
+ * Removal of Hqub.MusicBrainz.Entities.Collections namespace.
+ * Removal of Area.IsoCodes property (use Iso1Codes or Iso2Codes instead).
- Version 3.1 changes:
+ Other changes in version 4.0:
- * Remove net6.0 target and add net8.0
- * Remove obsolete serialization support from WebServiceException
- * Improve cache interface (add Contains(request) method)
- * Improve fluent API requests (add ToString() method returning the URL path of the request)
-
- Version 3.2 changes:
-
- * Add support for label lookup, search and browse
- * Add release events info to releases
- * Improve documentation
+ * Introduce IEntity as base interface for all entity classes supported by services.
+ * Add support for Area relationships.
+ * Add support for Area lookup to MusicBrainz client.
+ * Add missing Release.Packaging property.
diff --git a/src/Hqub.MusicBrainz/MusicBrainzClient.cs b/src/Hqub.MusicBrainz/MusicBrainzClient.cs
index 55b8576..3af77c7 100644
--- a/src/Hqub.MusicBrainz/MusicBrainzClient.cs
+++ b/src/Hqub.MusicBrainz/MusicBrainzClient.cs
@@ -139,7 +139,7 @@ class ResponseError
{
/// The error message.
[DataMember(Name = "error")]
- public string Message;
+ public string Message { get; set; }
}
internal async Task GetAsync(string url, CancellationToken ct = default)
diff --git a/src/Hqub.MusicBrainz/Resources/Constants.Designer.cs b/src/Hqub.MusicBrainz/Resources/Constants.Designer.cs
index f9b7756..4857e3d 100644
--- a/src/Hqub.MusicBrainz/Resources/Constants.Designer.cs
+++ b/src/Hqub.MusicBrainz/Resources/Constants.Designer.cs
@@ -70,7 +70,7 @@ internal static string ArtistQueryParams {
}
///
- /// Looks up a localized string similar to +official+promotion+bootleg+pseudo-release+.
+ /// Looks up a localized string similar to +official+promotion+bootleg+pseudo-release+withdrawn+cancelled+.
///
internal static string BrowseStatus {
get {
@@ -79,7 +79,7 @@ internal static string BrowseStatus {
}
///
- /// Looks up a localized string similar to +nat+album+single+ep+compilation+soundtrack+spokenword+interview+audiobook+live+remix+other+.
+ /// Looks up a localized string similar to +album+single+ep+broadcast+audiobook+compilation+demo+dj-mix+soundtrack+spokenword+interview+live+remix+other+.
///
internal static string BrowseType {
get {
diff --git a/src/Hqub.MusicBrainz/Resources/Constants.resx b/src/Hqub.MusicBrainz/Resources/Constants.resx
index 0c0bb24..28d2df0 100644
--- a/src/Hqub.MusicBrainz/Resources/Constants.resx
+++ b/src/Hqub.MusicBrainz/Resources/Constants.resx
@@ -121,10 +121,10 @@
-alias-primary_alias-area-arid-artist-artistaccent-alias-begin-beginarea-comment-country-end-endarea-ended-gender-ipi-isni-sortname-tag-type-
- +official+promotion+bootleg+pseudo-release+
+ +official+promotion+bootleg+pseudo-release+withdrawn+cancelled+
- +nat+album+single+ep+compilation+soundtrack+spokenword+interview+audiobook+live+remix+other+
+ +album+single+ep+broadcast+audiobook+compilation+demo+dj-mix+soundtrack+spokenword+interview+live+remix+other+
-alias-area-begin-code-comment-country-end-ended-ipi-isni-label-labelaccent-laid-release_count-sortname-tag-type-
diff --git a/src/Hqub.MusicBrainz/UrlBuilder.cs b/src/Hqub.MusicBrainz/UrlBuilder.cs
index 28f0681..0944d46 100644
--- a/src/Hqub.MusicBrainz/UrlBuilder.cs
+++ b/src/Hqub.MusicBrainz/UrlBuilder.cs
@@ -10,9 +10,12 @@ namespace Hqub.MusicBrainz
///
internal class UrlBuilder
{
- private const string LookupTemplate = "{0}/{1}/?inc={2}&fmt=json";
- private const string BrowseTemplate = "{0}?{1}={2}&limit={3}&offset={4}&inc={5}&fmt=json";
- private const string SearchTemplate = "{0}?query={1}&limit={2}&offset={3}&fmt=json";
+ private const string LookupTemplate = "{0}/{1}?fmt=json";
+ private const string BrowseTemplate = "{0}?{1}={2}&fmt=json";
+ private const string SearchTemplate = "{0}?query={1}&fmt=json";
+
+ private const int DEFAULT_LIMIT = 25;
+ private const int DEFAULT_OFFSET = 0;
private readonly bool validate;
@@ -26,15 +29,14 @@ public UrlBuilder(bool clientSideValidation)
///
public string CreateLookupUrl(string entity, string mbid, params string[] inc)
{
- return CreateLookupUrl(entity, mbid, string.Join("+", inc));
- }
+ var url = string.Format(LookupTemplate, entity, mbid);
- ///
- /// Creates a web service lookup template.
- ///
- private string CreateLookupUrl(string entity, string mbid, string inc)
- {
- return string.Format(LookupTemplate, entity, mbid, inc);
+ if (inc != null && inc.Length > 0)
+ {
+ url += "&inc=" + string.Join("+", inc);
+ }
+
+ return url;
}
///
@@ -42,7 +44,7 @@ private string CreateLookupUrl(string entity, string mbid, string inc)
///
public string CreateBrowseUrl(string entity, string relatedEntity, string mbid, int limit, int offset, params string[] inc)
{
- return CreateBrowseUrl(entity, relatedEntity, mbid, limit, offset, string.Join("+", inc));
+ return CreateBrowseUrl(entity, relatedEntity, mbid, null, null, limit, offset, inc);
}
///
@@ -51,39 +53,33 @@ public string CreateBrowseUrl(string entity, string relatedEntity, string mbid,
public string CreateBrowseUrl(string entity, string relatedEntity, string mbid, string type, string status,
int limit, int offset, params string[] inc)
{
- var url = CreateBrowseUrl(entity, relatedEntity, mbid, limit, offset, string.Join("+", inc));
+ var url = string.Format(BrowseTemplate, entity, relatedEntity, mbid);
- if (validate && !ValidateBrowseParam(Resources.Constants.BrowseStatus, status))
- {
- throw new ArgumentException(string.Format(Resources.Messages.InvalidQueryValue, status, "status"));
- }
-
- if (validate && !ValidateBrowseParam(Resources.Constants.BrowseType, type))
- {
- throw new ArgumentException(string.Format(Resources.Messages.InvalidQueryValue, type, "type"));
- }
+ url = AddOptionalParams(url, limit, offset, inc);
if (!string.IsNullOrEmpty(type))
{
+ if (validate && !ValidateBrowseParam(Resources.Constants.BrowseType, type))
+ {
+ throw new ArgumentException(string.Format(Resources.Messages.InvalidQueryValue, type, "type"));
+ }
+
url += "&type=" + type;
}
if (!string.IsNullOrEmpty(status))
{
+ if (validate && !ValidateBrowseParam(Resources.Constants.BrowseStatus, status))
+ {
+ throw new ArgumentException(string.Format(Resources.Messages.InvalidQueryValue, status, "status"));
+ }
+
url += "&status=" + status;
}
return url;
}
- ///
- /// Creates a web service browse template.
- ///
- private string CreateBrowseUrl(string entity, string relatedEntity, string mbid, int limit, int offset, string inc)
- {
- return string.Format(BrowseTemplate, entity, relatedEntity, mbid, limit, offset, inc);
- }
-
///
/// Creates a web service search template.
///
@@ -91,7 +87,31 @@ public string CreateSearchUrl(string entity, string query, int limit, int offset
{
query = WebUtility.UrlEncode(query);
- return string.Format(SearchTemplate, entity, query, limit, offset);
+ var url = string.Format(SearchTemplate, entity, query);
+
+ url = AddOptionalParams(url, limit, offset, null);
+
+ return url;
+ }
+
+ private string AddOptionalParams(string url, int limit, int offset, params string[] inc)
+ {
+ if (limit != DEFAULT_LIMIT)
+ {
+ url += "&limit=" + limit;
+ }
+
+ if (offset != DEFAULT_OFFSET)
+ {
+ url += "&offset=" + offset;
+ }
+
+ if (inc != null && inc.Length > 0)
+ {
+ url += "&inc=" + string.Join("+", inc);
+ }
+
+ return url;
}
private bool ValidateBrowseParam(string availableParams, string value)
@@ -103,10 +123,10 @@ private bool ValidateBrowseParam(string availableParams, string value)
if (value.IndexOf('|') > 0)
{
- return value.Split('|').All(s => availableParams.IndexOf("+" + s + "+") >= 0);
+ return value.Split('|').All(s => availableParams.Contains("+" + s + "+"));
}
- return availableParams.IndexOf("+" + value + "+") >= 0;
+ return availableParams.Contains("+" + value + "+");
}
}
}
diff --git a/src/Hqub.MusicBrainz/WebServiceException.cs b/src/Hqub.MusicBrainz/WebServiceException.cs
index f0685f8..e72b0e7 100644
--- a/src/Hqub.MusicBrainz/WebServiceException.cs
+++ b/src/Hqub.MusicBrainz/WebServiceException.cs
@@ -12,13 +12,13 @@ public sealed class WebServiceException : Exception
///
/// Gets the response HTTP status code.
///
- public readonly HttpStatusCode StatusCode;
+ public HttpStatusCode StatusCode { get; }
///
/// Gets the requested uri.
///
- public readonly string Uri;
-
+ public string Uri { get; }
+
///
/// Initializes a new instance of the class.
///