diff --git a/src/Piwigo.Client.Tests/Piwigo.Client.Tests.csproj b/src/Piwigo.Client.Tests/Piwigo.Client.Tests.csproj
index 307113d..2da2de7 100644
--- a/src/Piwigo.Client.Tests/Piwigo.Client.Tests.csproj
+++ b/src/Piwigo.Client.Tests/Piwigo.Client.Tests.csproj
@@ -56,7 +56,13 @@
TagApiTests.cs
-
+
+ Always
+
+
+ TagApiTests.cs
+
+
TagApiTests.cs
diff --git a/src/Piwigo.Client.Tests/TagApi.Merge.json b/src/Piwigo.Client.Tests/TagApi.Merge.json
new file mode 100644
index 0000000..14a7dbf
--- /dev/null
+++ b/src/Piwigo.Client.Tests/TagApi.Merge.json
@@ -0,0 +1,13 @@
+{
+ "stat": "ok",
+ "result": {
+ "destination_tag": 1,
+ "deleted_tag": [
+ 3
+ ],
+ "images_in_merged_tag": [
+ "4",
+ "3"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/Piwigo.Client.Tests/TagApiTests.Duplicate_should_pass_correct_parameters.verified.txt b/src/Piwigo.Client.Tests/TagApiTests.DuplicateAsync_should_pass_correct_parameters.verified.txt
similarity index 100%
rename from src/Piwigo.Client.Tests/TagApiTests.Duplicate_should_pass_correct_parameters.verified.txt
rename to src/Piwigo.Client.Tests/TagApiTests.DuplicateAsync_should_pass_correct_parameters.verified.txt
diff --git a/src/Piwigo.Client.Tests/TagApiTests.MergeAsync_should_pass_correct_parameters.verified.txt b/src/Piwigo.Client.Tests/TagApiTests.MergeAsync_should_pass_correct_parameters.verified.txt
new file mode 100644
index 0000000..51f5b8b
--- /dev/null
+++ b/src/Piwigo.Client.Tests/TagApiTests.MergeAsync_should_pass_correct_parameters.verified.txt
@@ -0,0 +1,10 @@
+{
+ destination_tag: 1,
+ deleted_tag: [
+ 3
+ ],
+ images_in_merged_tag: [
+ 4,
+ 3
+ ]
+}
\ No newline at end of file
diff --git a/src/Piwigo.Client.Tests/TagApiTests.cs b/src/Piwigo.Client.Tests/TagApiTests.cs
index 5a4c070..3bb2fa6 100644
--- a/src/Piwigo.Client.Tests/TagApiTests.cs
+++ b/src/Piwigo.Client.Tests/TagApiTests.cs
@@ -78,4 +78,19 @@ public class TagApiTests : ApiTestsBase
await Verify(response);
}
+
+ [Test]
+ public async Task MergeAsync_should_pass_correct_parameters()
+ {
+ await SetJsonResultFromFileAsync("TagApi.Merge.json");
+
+ var response = await _tagApi.MergeAsync(1, 4, ApiToken);
+
+ CorrectMethodShouldGetCalled("pwg.tags.merge");
+ CorrectParamShouldGetSent("destination_tag_id", "1");
+ CorrectParamShouldGetSent("merge_tag_id", "4");
+ CorrectParamShouldGetSent("pwg_token", ApiToken);
+
+ await Verify(response);
+ }
}
\ No newline at end of file
diff --git a/src/Piwigo.Client/Tags/ITagApi.cs b/src/Piwigo.Client/Tags/ITagApi.cs
index 33bf446..f89df5b 100644
--- a/src/Piwigo.Client/Tags/ITagApi.cs
+++ b/src/Piwigo.Client/Tags/ITagApi.cs
@@ -53,4 +53,16 @@ public interface ITagApi
///
/// all tags with at least one image assigned
Task> GetListAsync(bool? sortByCounter = null, CancellationToken cancellationToken = default);
+
+ ///
+ /// Merges two existing tags
+ ///
+ /// The tag where the images should get merged into
+ /// The tag that will get obsolete
+ /// The API token that can be read from
+ ///
+ ///
+ ///
+ ///
+ Task MergeAsync(int targetTagId, int sourceTagId, string apiToken, CancellationToken cancellationToken = default);
}
\ No newline at end of file
diff --git a/src/Piwigo.Client/Tags/MergeResult.cs b/src/Piwigo.Client/Tags/MergeResult.cs
new file mode 100644
index 0000000..caff4fe
--- /dev/null
+++ b/src/Piwigo.Client/Tags/MergeResult.cs
@@ -0,0 +1,15 @@
+using Newtonsoft.Json;
+
+namespace Piwigo.Client.Tags;
+
+public record MergeResult
+{
+ [JsonProperty("destination_tag")]
+ public int DestinationTagId { get; init; }
+
+ [JsonProperty("deleted_tag")]
+ public IReadOnlyCollection? DeletedTags { get; init; }
+
+ [JsonProperty("images_in_merged_tag")]
+ public IReadOnlyCollection? ImagesInMergedTag { get; init; }
+}
\ No newline at end of file
diff --git a/src/Piwigo.Client/Tags/TagApi.cs b/src/Piwigo.Client/Tags/TagApi.cs
index f027209..b3544be 100644
--- a/src/Piwigo.Client/Tags/TagApi.cs
+++ b/src/Piwigo.Client/Tags/TagApi.cs
@@ -62,4 +62,18 @@ public class TagApi : ITagApi
return response.Result;
}
+
+ public async Task MergeAsync(int targetTagId, int sourceTagId, string apiToken, CancellationToken cancellationToken = default)
+ {
+ var formParams = new Dictionary
+ {
+ { "destination_tag_id", targetTagId.ToString() },
+ { "merge_tag_id", sourceTagId.ToString() },
+ { "pwg_token", apiToken }
+ };
+
+ var response = await _context.PostAsync>(_logger, "pwg.tags.merge", formParams, cancellationToken);
+
+ return response.Result;
+ }
}
\ No newline at end of file