diff --git a/NiconicoToolkit.Shared/NiconicoToolkit.Shared.projitems b/NiconicoToolkit.Shared/NiconicoToolkit.Shared.projitems
index 21d6c1b..aa1a0de 100644
--- a/NiconicoToolkit.Shared/NiconicoToolkit.Shared.projitems
+++ b/NiconicoToolkit.Shared/NiconicoToolkit.Shared.projitems
@@ -235,7 +235,10 @@
-
+
+
+
+
@@ -243,7 +246,4 @@
-
-
-
\ No newline at end of file
diff --git a/NiconicoToolkit.Shared/Video/Watch/NV_Comment/CommentSubClient.cs b/NiconicoToolkit.Shared/Video/Watch/NV_Comment/CommentSubClient.cs
deleted file mode 100644
index 4aff71a..0000000
--- a/NiconicoToolkit.Shared/Video/Watch/NV_Comment/CommentSubClient.cs
+++ /dev/null
@@ -1,434 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.Json.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Text.Json;
-using System.Linq;
-#if WINDOWS_UWP
-using Windows.Web.Http;
-using Windows.Web.Http.Headers;
-#else
-using System.Net.Http;
-using System.Net.Http.Headers;
-#endif
-
-namespace NiconicoToolkit.Video.Watch.NV_Comment;
-
-public sealed class NvCommentSubClient
-{
- private readonly NiconicoContext _context;
- private readonly JsonSerializerOptions _option;
-
- public NvCommentSubClient(NiconicoContext context, JsonSerializerOptions option)
- {
- _context = context;
- _option = option;
- }
-
-
- const string NVCommentApiUrl = "https://nvcomment.nicovideo.jp/v1/threads";
-
- ///
- /// 動画に投稿されたコメントを取得します。
- ///
- /// VideoClient.VideoWatch.GetInitialWatchDataAsync() のレスポンスデータに含まれる NvComment を指定します。
- ///
- ///
- public async Task GetCommentsAsync(NvComment videoComment, CancellationToken ct = default)
- {
- string requestParamsJson = JsonSerializer.Serialize(new ThreadRequestContainer()
- {
- ThreadKey = videoComment.ThreadKey,
- Params = new ThreadRequestContainer.ThreadRequestParams()
- {
- Targets = videoComment.Params.Targets,
- Language = videoComment.Params.Language,
- }
- });
- return await _context.SendJsonAsAsync(
- HttpMethod.Post,
- NVCommentApiUrl,
- requestParamsJson,
- ct: ct
- );
- }
-
- ///
- /// 動画に投稿されたコメントを取得します。
- ///
- ///
- /// 取得対象とするコメント郡を指定します。参考:ThreadTargetIdConstants
- ///
- ///
- ///
- /// VideoClient.VideoWatch.GetInitialWatchDataAsync() のレスポンスデータに含まれる NvComment のデータを引数に渡してください。
- public async Task GetCommentsAsync(string threadKey, IEnumerable targets, string language, CancellationToken ct = default)
- {
- string requestParamsJson = JsonSerializer.Serialize(new ThreadRequestContainer()
- {
- ThreadKey = threadKey,
- Params = new ThreadRequestContainer.ThreadRequestParams()
- {
- Targets = targets.ToList(),
- Language = language,
- }
- });
- return await _context.SendJsonAsAsync(
- HttpMethod.Post,
- NVCommentApiUrl,
- requestParamsJson,
- ct: ct
- );
- }
-
- ///
- /// スレッドにコメントを送信するためのポストキーを取得します。
- ///
- /// スレッドID。WatchApiResponse.WatchApiData.Comment.Threads から取得できます。
- ///
- [RequireLogin]
- public async Task GetPostKeyAsync(string threadId, CancellationToken ct = default)
- {
- return await _context.GetJsonAsAsync(
- $"https://nvapi.nicovideo.jp/v1/comment/keys/post?threadId={threadId}", ct: ct
- );
- }
-
- ///
- /// 動画の指定スレッドに対してコメント投稿を送信します。
- ///
- /// スレッドID。WatchApiResponse.WatchApiData.Comment.Threads から取得できます。
- /// 動画ID。stringのままでもVideoIdへの暗黙の型変換により指定可能です。
- /// 184などのコマンドを指定します
- /// https://dic.nicovideo.jp/a/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89
- /// コメント本文。Uriエンコードは不要です。
- /// コメントする動画時間をマイクロ秒単位で指定します。動画秒数に対して1000を掛け算することでマイクロ秒へ変換できます。
- /// GetPostKeyAsync()を使用して予め取得してください。
- ///
- ///
- /// パラメータエラーによる失敗は全て INVALID_PARAMETER としてまとめられるため、何が間違いかは判別できません。
- [RequireLogin]
- public async Task PostCommentAsync(
- string threadId,
- VideoId videoId,
- IEnumerable commands,
- string comment,
- int vPosMs,
- string postKey,
- CancellationToken ct = default
- )
- {
- string requestParamsJson = JsonSerializer.Serialize(new ThreadPostRequest()
- {
- VideoId = videoId.ToString(),
- Commands = commands.ToList(),
- Body = comment,
- VposMs = vPosMs,
- PostKey = postKey,
- });
-
- return await _context.SendJsonAsAsync(HttpMethod.Post,
- $"{NVCommentApiUrl}/{threadId}/comments", requestParamsJson, ct: ct);
- }
-
-
- [RequireLogin]
- public async Task GetEasyPostKeyAsync(string threadId, CancellationToken ct = default)
- {
- return await _context.GetJsonAsAsync(
- $"https://nvapi.nicovideo.jp/v1/comment/keys/post-easy?threadId={threadId}", ct: ct
- );
- }
-
- [RequireLogin]
- public async Task EasyPostCommentAsync(
- string threadId,
- VideoId videoId,
- string comment,
- int vPosMs,
- string easyPostKey,
- CancellationToken ct = default
- )
- {
- string requestParamsJson = JsonSerializer.Serialize(new ThreadEasyPostRequest()
- {
- VideoId = videoId.ToString(),
- Body = comment,
- VposMs = vPosMs,
- EasyPostKey = easyPostKey,
- });
-
- return await _context.SendJsonAsAsync(HttpMethod.Post,
- $"{NVCommentApiUrl}/{threadId}/easy-comments", requestParamsJson, ct: ct);
- }
-
- [RequireLogin]
- public async Task GetDeleteKeyAsync(string threadId, string fork, CancellationToken ct = default)
- {
- return await _context.GetJsonAsAsync(
- $"https://nvapi.nicovideo.jp/v1/comment/keys/delete?threadId={threadId}&fork={fork}", ct: ct
- );
- }
-
- [RequireLogin]
- public async Task DeleteCommentAsync(
- VideoId videoId,
- string threadId,
- string fork,
- int commentNumber,
- string language,
- string deleteKey,
- CancellationToken ct = default
- )
- {
- string requestParamsJson = JsonSerializer.Serialize(new ThreadDeleteRequest()
- {
- VideoId = videoId.ToString(),
- Fork = fork,
- DeleteKey = deleteKey,
- Language = language,
- Targets = new () { new () { Number = commentNumber }}
- });
-
- return await _context.SendJsonAsAsync(HttpMethod.Put,
- $"{NVCommentApiUrl}/{threadId}/comment-comment-owner-deletions", requestParamsJson, ct: ct);
- }
-}
-
-public sealed class ThreadDeleteRequest
-{
- [JsonPropertyName("videoId")]
- public string VideoId { get; set; }
-
- [JsonPropertyName("deleteKey")]
- public string DeleteKey { get; set; }
-
- [JsonPropertyName("language")]
- public string Language { get; set; }
-
- [JsonPropertyName("targets")]
- public List Targets { get; set; }
-
- [JsonPropertyName("fork")]
- public string Fork { get; set; }
-
- public sealed class ThreadDeleteTarget
- {
- [JsonPropertyName("no")]
- public int Number { get; set; }
-
- [JsonPropertyName("operation")]
- public string Operation { get; set; } = "DELETE";
- }
-}
-
-public sealed class ThreadDeleteResponse : ResponseWithMeta
-{
-
-}
-
-
-public static class ThreadTargetForkConstants
-{
- public const string Easy = "easy";
- public const string Main = "main";
- public const string Owner = "owner";
-}
-
-
-public sealed class ThreadPostKeyResponse : ResponseWithMeta
-{
- [JsonPropertyName("data")]
- public ThreadPostKeyData? Data { get; set; }
-
- public sealed class ThreadPostKeyData
- {
- [JsonPropertyName("postKey")]
- public string PostKey { get; set; }
- }
-}
-
-public sealed class ThreadPostRequest
-{
- [JsonPropertyName("videoId")]
- public string VideoId { get; set; }
-
- [JsonPropertyName("commands")]
- public List Commands { get; set; }
-
- [JsonPropertyName("body")]
- public string Body { get; set; }
-
- [JsonPropertyName("vposMs")]
- public int VposMs { get; set; }
-
- [JsonPropertyName("postKey")]
- public string PostKey { get; set; }
-}
-
-public sealed class ThreadEasyPostRequest
-{
- [JsonPropertyName("videoId")]
- public string VideoId { get; set; }
-
- [JsonPropertyName("body")]
- public string Body { get; set; }
-
- [JsonPropertyName("vposMs")]
- public int VposMs { get; set; }
-
- [JsonPropertyName("postEasyKey")]
- public string EasyPostKey { get; set; }
-}
-
-public sealed class ThreadPostResponse : ResponseWithMeta
-{
- [JsonPropertyName("data")]
- public ThreadPostResponseData? Data { get; set; }
-
- public sealed class ThreadPostResponseData
- {
- [JsonPropertyName("id")]
- public string Id { get; set; }
-
- [JsonPropertyName("no")]
- public int Number { get; set; }
- }
-}
-
-public sealed class ThreadDeleteKeyResponse : ResponseWithMeta
-{
- [JsonPropertyName("data")]
- public ThreadDeleteKeyData? Data { get; set; }
-
- public sealed class ThreadDeleteKeyData
- {
- [JsonPropertyName("deleteKey")]
- public string DeleteKey { get; set; }
- }
-}
-
-public sealed class ThreadEasyPostKeyResponse : ResponseWithMeta
-{
- [JsonPropertyName("data")]
- public ThreadEasyPostKeyData? Data { get; set; }
-
- public sealed class ThreadEasyPostKeyData
- {
- [JsonPropertyName("postEasyKey")]
- public string EasyPostKey { get; set; }
- }
-}
-
-public class ThreadRequestContainer
-{
- [JsonPropertyName("params")]
- public ThreadRequestParams Params { get; set; }
-
- [JsonPropertyName("threadKey")]
- public string ThreadKey { get; set; }
-
- [JsonPropertyName("additionals")]
- public ThreadRequestAdditionals Additionals { get; set; } = new ThreadRequestAdditionals();
-
-
- public class ThreadRequestAdditionals
- {
- }
-
- public class ThreadRequestParams
- {
- [JsonPropertyName("targets")]
- public List Targets { get; set; }
-
- [JsonPropertyName("language")]
- public string Language { get; set; }
- }
-}
-
-
-public class ThreadResponseContainer : ResponseWithMeta
-{
- [JsonPropertyName("data")]
- public ThreadResponseData Data { get; set; }
-}
-
-
-// Root myDeserializedClass = JsonSerializer.Deserialize(myJsonResponse);
-public class Comment
-{
- [JsonPropertyName("id")]
- public string Id { get; set; }
-
- [JsonPropertyName("no")]
- public int No { get; set; }
-
- [JsonPropertyName("vposMs")]
- public int VposMs { get; set; }
-
- [JsonPropertyName("body")]
- public string Body { get; set; }
-
- [JsonPropertyName("commands")]
- public List Commands { get; set; }
-
- [JsonPropertyName("userId")]
- public string UserId { get; set; }
-
- [JsonPropertyName("isPremium")]
- public bool IsPremium { get; set; }
-
- [JsonPropertyName("score")]
- public int Score { get; set; }
-
- [JsonPropertyName("postedAt")]
- public DateTime PostedAt { get; set; }
-
- [JsonPropertyName("nicoruCount")]
- public int NicoruCount { get; set; }
-
- [JsonPropertyName("nicoruId")]
- public object NicoruId { get; set; }
-
- [JsonPropertyName("source")]
- public string Source { get; set; }
-
- [JsonPropertyName("isMyPost")]
- public bool IsMyPost { get; set; }
-}
-
-public class ThreadResponseData
-{
- [JsonPropertyName("globalComments")]
- public List GlobalComments { get; set; }
-
- [JsonPropertyName("threads")]
- public List Threads { get; set; }
-}
-
-public class GlobalComment
-{
- [JsonPropertyName("id")]
- public string Id { get; set; }
-
- [JsonPropertyName("count")]
- public int Count { get; set; }
-}
-
-public class Thread
-{
- [JsonPropertyName("id")]
- public string Id { get; set; }
-
- [JsonPropertyName("fork")]
- public string Fork { get; set; }
-
- [JsonPropertyName("commentCount")]
- public int CommentCount { get; set; }
-
- [JsonPropertyName("comments")]
- public List Comments { get; set; }
-}
-
diff --git a/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Core.cs b/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Core.cs
new file mode 100644
index 0000000..86dcbc9
--- /dev/null
+++ b/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Core.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Text.Json;
+using System.Linq;
+#if WINDOWS_UWP
+using Windows.Web.Http;
+using Windows.Web.Http.Headers;
+#else
+using System.Net.Http;
+using System.Net.Http.Headers;
+#endif
+
+namespace NiconicoToolkit.Video.Watch.NV_Comment;
+
+public sealed partial class NvCommentSubClient
+{
+ private readonly NiconicoContext _context;
+ private readonly JsonSerializerOptions _option;
+
+ public NvCommentSubClient(NiconicoContext context, JsonSerializerOptions option)
+ {
+ _context = context;
+ _option = option;
+ }
+
+ public const string NVCommentThreadsUrl = "https://nvcomment.nicovideo.jp/v1/threads";
+ public const string NvApiCommentKeysUrl = "https://nvapi.nicovideo.jp/v1/comment/keys/";
+}
+
+public static class ThreadTargetForkConstants
+{
+ public const string Easy = "easy";
+ public const string Main = "main";
+ public const string Owner = "owner";
+}
+
+
+
+
diff --git a/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Delete.cs b/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Delete.cs
new file mode 100644
index 0000000..fb37ca4
--- /dev/null
+++ b/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Delete.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+using System.Threading;
+#if WINDOWS_UWP
+using Windows.Web.Http;
+using Windows.Web.Http.Headers;
+#else
+using System.Net.Http;
+using System.Net.Http.Headers;
+#endif
+
+namespace NiconicoToolkit.Video.Watch.NV_Comment;
+
+public partial class NvCommentSubClient
+{
+ ///
+ /// 自らが投稿したコメントを削除する際のキーを取得します。
+ ///
+ ///
+ /// fork または forkLabel を指定。参考:ThreadTargetForkConstants
+ ///
+ ///
+ [RequireLogin]
+ public async Task GetDeleteKeyAsync(string threadId, string fork, CancellationToken ct = default)
+ {
+ return await _context.GetJsonAsAsync(
+ $"{NvApiCommentKeysUrl}delete?threadId={threadId}&fork={fork}", ct: ct
+ );
+ }
+
+
+ ///
+ /// 自らが投稿したコメントを削除します。IsMyPost が true のコメント番号を指定して削除します。
+ ///
+ ///
+ ///
+ ///
+ /// コメントの番号("no")
+ ///
+ /// GetDeleteKeyAsync() によって得られる削除キーを指定します。
+ ///
+ ///
+ [RequireLogin]
+ public async Task DeleteCommentAsync(
+ VideoId videoId,
+ string threadId,
+ string fork,
+ int commentNumber,
+ string language,
+ string deleteKey,
+ CancellationToken ct = default
+ )
+ {
+ string requestParamsJson = JsonSerializer.Serialize(new ThreadDeleteRequest()
+ {
+ VideoId = videoId.ToString(),
+ Fork = fork,
+ DeleteKey = deleteKey,
+ Language = language,
+ Targets = new() { new() { Number = commentNumber } }
+ });
+
+ return await _context.SendJsonAsAsync(HttpMethod.Put,
+ $"{NVCommentThreadsUrl}/{threadId}/comment-comment-owner-deletions", requestParamsJson, ct: ct);
+ }
+}
+
+
+
+public sealed class ThreadDeleteRequest
+{
+ [JsonPropertyName("videoId")]
+ public string VideoId { get; set; }
+
+ [JsonPropertyName("deleteKey")]
+ public string DeleteKey { get; set; }
+
+ [JsonPropertyName("language")]
+ public string Language { get; set; }
+
+ [JsonPropertyName("targets")]
+ public List Targets { get; set; }
+
+ [JsonPropertyName("fork")]
+ public string Fork { get; set; }
+
+ public sealed class ThreadDeleteTarget
+ {
+ [JsonPropertyName("no")]
+ public int Number { get; set; }
+
+ [JsonPropertyName("operation")]
+ public string Operation { get; set; } = "DELETE";
+ }
+}
+
+public sealed class ThreadDeleteResponse : ResponseWithMeta
+{
+
+}
+
diff --git a/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Get.cs b/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Get.cs
new file mode 100644
index 0000000..f80e1b6
--- /dev/null
+++ b/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Get.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using System.Threading;
+using System.Text.Json.Serialization;
+#if WINDOWS_UWP
+using Windows.Web.Http;
+using Windows.Web.Http.Headers;
+#else
+using System.Net.Http;
+using System.Net.Http.Headers;
+#endif
+
+namespace NiconicoToolkit.Video.Watch.NV_Comment;
+
+public partial class NvCommentSubClient
+{
+
+ ///
+ /// 動画に投稿されたコメントを取得します。
+ ///
+ /// VideoClient.VideoWatch.GetInitialWatchDataAsync() のレスポンスデータに含まれる NvComment を指定します。
+ ///
+ ///
+ public async Task GetCommentsAsync(NvComment videoComment, CancellationToken ct = default)
+ {
+ string requestParamsJson = JsonSerializer.Serialize(new ThreadRequest()
+ {
+ ThreadKey = videoComment.ThreadKey,
+ Params = new ThreadRequest.ThreadRequestParams()
+ {
+ Targets = videoComment.Params.Targets,
+ Language = videoComment.Params.Language,
+ }
+ });
+ return await _context.SendJsonAsAsync(
+ HttpMethod.Post,
+ NVCommentThreadsUrl,
+ requestParamsJson,
+ ct: ct
+ );
+ }
+
+ ///
+ /// 動画に投稿されたコメントを取得します。
+ ///
+ /// VideoClient.VideoWatch.GetInitialWatchDataAsync() のレスポンスデータに含まれる NvComment を指定します。
+ /// 取得対象とするコメントForkを指定します。参考:ThreadTargetIdConstants
+ ///
+ ///
+ /// VideoClient.VideoWatch.GetInitialWatchDataAsync() のレスポンスデータに含まれる NvComment のデータを引数に渡してください。
+ public async Task GetCommentsAsync(NvComment videoComment, IEnumerable targetForks, CancellationToken ct = default)
+ {
+ var forks = targetForks.ToHashSet();
+ string requestParamsJson = JsonSerializer.Serialize(new ThreadRequest()
+ {
+ ThreadKey = videoComment.ThreadKey,
+ Params = new ThreadRequest.ThreadRequestParams()
+ {
+ Targets = videoComment.Params.Targets.Where(x => forks.Contains(x.Fork)).ToList(),
+ Language = videoComment.Params.Language,
+ }
+ });
+ return await _context.SendJsonAsAsync(
+ HttpMethod.Post,
+ NVCommentThreadsUrl,
+ requestParamsJson,
+ ct: ct
+ );
+ }
+}
+
+
+class ThreadRequest
+{
+ [JsonPropertyName("params")]
+ public ThreadRequestParams Params { get; set; }
+
+ [JsonPropertyName("threadKey")]
+ public string ThreadKey { get; set; }
+
+ [JsonPropertyName("additionals")]
+ public ThreadRequestAdditionals Additionals { get; set; } = new ThreadRequestAdditionals();
+
+
+ public class ThreadRequestAdditionals
+ {
+ }
+
+ public class ThreadRequestParams
+ {
+ [JsonPropertyName("targets")]
+ public List Targets { get; set; }
+
+ [JsonPropertyName("language")]
+ public string Language { get; set; }
+ }
+}
+
+
+public class ThreadResponse : ResponseWithMeta
+{
+ [JsonPropertyName("data")]
+ public ThreadResponseData Data { get; set; }
+
+
+ public class ThreadResponseData
+ {
+ [JsonPropertyName("globalComments")]
+ public List GlobalComments { get; set; }
+
+ [JsonPropertyName("threads")]
+ public List Threads { get; set; }
+ }
+
+ public class GlobalComment
+ {
+ [JsonPropertyName("id")]
+ public string Id { get; set; }
+
+ [JsonPropertyName("count")]
+ public int Count { get; set; }
+ }
+
+ public class Thread
+ {
+ [JsonPropertyName("id")]
+ public string Id { get; set; }
+
+ [JsonPropertyName("fork")]
+ public string Fork { get; set; }
+
+ [JsonPropertyName("commentCount")]
+ public int CommentCount { get; set; }
+
+ [JsonPropertyName("comments")]
+ public List Comments { get; set; }
+ }
+
+ public class Comment
+ {
+ [JsonPropertyName("id")]
+ public string Id { get; set; }
+
+ [JsonPropertyName("no")]
+ public int No { get; set; }
+
+ [JsonPropertyName("vposMs")]
+ public int VposMs { get; set; }
+
+ [JsonPropertyName("body")]
+ public string Body { get; set; }
+
+ [JsonPropertyName("commands")]
+ public List Commands { get; set; }
+
+ [JsonPropertyName("userId")]
+ public string UserId { get; set; }
+
+ [JsonPropertyName("isPremium")]
+ public bool IsPremium { get; set; }
+
+ [JsonPropertyName("score")]
+ public int Score { get; set; }
+
+ [JsonPropertyName("postedAt")]
+ public DateTime PostedAt { get; set; }
+
+ [JsonPropertyName("nicoruCount")]
+ public int NicoruCount { get; set; }
+
+ [JsonPropertyName("nicoruId")]
+ public object NicoruId { get; set; }
+
+ [JsonPropertyName("source")]
+ public string Source { get; set; }
+
+ [JsonPropertyName("isMyPost")]
+ public bool IsMyPost { get; set; }
+ }
+}
+
diff --git a/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Post.cs b/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Post.cs
new file mode 100644
index 0000000..dbd70a7
--- /dev/null
+++ b/NiconicoToolkit.Shared/Video/Watch/NV_Comment/NvCommentSubClient.Post.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using System.Threading;
+using System.Text.Json.Serialization;
+#if WINDOWS_UWP
+using Windows.Web.Http;
+using Windows.Web.Http.Headers;
+#else
+using System.Net.Http;
+using System.Net.Http.Headers;
+#endif
+
+namespace NiconicoToolkit.Video.Watch.NV_Comment;
+
+public partial class NvCommentSubClient
+{
+
+ ///
+ /// スレッドにコメントを送信するためのポストキーを取得します。
+ ///
+ /// スレッドID。WatchApiResponse.WatchApiData.Comment.Threads から取得できます。
+ ///
+ [RequireLogin]
+ public async Task GetPostKeyAsync(string threadId, CancellationToken ct = default)
+ {
+ return await _context.GetJsonAsAsync(
+ $"{NvApiCommentKeysUrl}post?threadId={threadId}", ct: ct
+ );
+ }
+
+ ///
+ /// 動画の指定スレッドに対してコメント投稿を送信します。
+ ///
+ /// スレッドID。WatchApiResponse.WatchApiData.Comment.Threads から取得できます。
+ /// 動画ID。stringのままでもVideoIdへの暗黙の型変換により指定可能です。
+ /// 184などのコマンドを指定します
+ /// https://dic.nicovideo.jp/a/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89
+ /// コメント本文。Uriエンコードは不要です。
+ /// コメントする動画時間をマイクロ秒単位で指定します。動画秒数に対して1000を掛け算することでマイクロ秒へ変換できます。
+ /// GetPostKeyAsync()を使用して予め取得してください。
+ ///
+ ///
+ /// パラメータエラーによる失敗は全て INVALID_PARAMETER としてまとめられるため、何が間違いかは判別できません。
+ [RequireLogin]
+ public async Task PostCommentAsync(
+ string threadId,
+ VideoId videoId,
+ IEnumerable commands,
+ string comment,
+ int vPosMs,
+ string postKey,
+ CancellationToken ct = default
+ )
+ {
+ string requestParamsJson = JsonSerializer.Serialize(new ThreadPostRequest()
+ {
+ VideoId = videoId.ToString(),
+ Commands = commands.ToList(),
+ Body = comment,
+ VposMs = vPosMs,
+ PostKey = postKey,
+ });
+
+ return await _context.SendJsonAsAsync(HttpMethod.Post,
+ $"{NVCommentThreadsUrl}/{threadId}/comments", requestParamsJson, ct: ct);
+ }
+
+
+ [RequireLogin]
+ public async Task GetEasyPostKeyAsync(string threadId, CancellationToken ct = default)
+ {
+ return await _context.GetJsonAsAsync(
+ $"{NvApiCommentKeysUrl}post-easy?threadId={threadId}", ct: ct
+ );
+ }
+
+ [RequireLogin]
+ public async Task EasyPostCommentAsync(
+ string threadId,
+ VideoId videoId,
+ string comment,
+ int vPosMs,
+ string easyPostKey,
+ CancellationToken ct = default
+ )
+ {
+ string requestParamsJson = JsonSerializer.Serialize(new ThreadEasyPostRequest()
+ {
+ VideoId = videoId.ToString(),
+ Body = comment,
+ VposMs = vPosMs,
+ EasyPostKey = easyPostKey,
+ });
+
+ return await _context.SendJsonAsAsync(HttpMethod.Post,
+ $"{NVCommentThreadsUrl}/{threadId}/easy-comments", requestParamsJson, ct: ct);
+ }
+}
+
+public sealed class ThreadPostKeyResponse : ResponseWithMeta
+{
+ [JsonPropertyName("data")]
+ public ThreadPostKeyData? Data { get; set; }
+
+ public sealed class ThreadPostKeyData
+ {
+ [JsonPropertyName("postKey")]
+ public string PostKey { get; set; }
+ }
+}
+
+sealed class ThreadPostRequest
+{
+ [JsonPropertyName("videoId")]
+ public string VideoId { get; set; }
+
+ [JsonPropertyName("commands")]
+ public List Commands { get; set; }
+
+ [JsonPropertyName("body")]
+ public string Body { get; set; }
+
+ [JsonPropertyName("vposMs")]
+ public int VposMs { get; set; }
+
+ [JsonPropertyName("postKey")]
+ public string PostKey { get; set; }
+}
+
+sealed class ThreadEasyPostRequest
+{
+ [JsonPropertyName("videoId")]
+ public string VideoId { get; set; }
+
+ [JsonPropertyName("body")]
+ public string Body { get; set; }
+
+ [JsonPropertyName("vposMs")]
+ public int VposMs { get; set; }
+
+ [JsonPropertyName("postEasyKey")]
+ public string EasyPostKey { get; set; }
+}
+
+public sealed class ThreadPostResponse : ResponseWithMeta
+{
+ [JsonPropertyName("data")]
+ public ThreadPostResponseData? Data { get; set; }
+
+ public sealed class ThreadPostResponseData
+ {
+ [JsonPropertyName("id")]
+ public string Id { get; set; }
+
+ [JsonPropertyName("no")]
+ public int Number { get; set; }
+ }
+}
+
+public sealed class ThreadDeleteKeyResponse : ResponseWithMeta
+{
+ [JsonPropertyName("data")]
+ public ThreadDeleteKeyData? Data { get; set; }
+
+ public sealed class ThreadDeleteKeyData
+ {
+ [JsonPropertyName("deleteKey")]
+ public string DeleteKey { get; set; }
+ }
+}
+
+public sealed class ThreadEasyPostKeyResponse : ResponseWithMeta
+{
+ [JsonPropertyName("data")]
+ public ThreadEasyPostKeyData? Data { get; set; }
+
+ public sealed class ThreadEasyPostKeyData
+ {
+ [JsonPropertyName("postEasyKey")]
+ public string EasyPostKey { get; set; }
+ }
+}
+
diff --git a/NuSpec/build_nuget.cmd b/NuSpec/build_nuget.cmd
index de067e3..5874570 100644
--- a/NuSpec/build_nuget.cmd
+++ b/NuSpec/build_nuget.cmd
@@ -1 +1 @@
-nuget pack Package.nuspec -outputDirectory "C:\Users\tor4k\local_nuget" -p version=0.3.1
\ No newline at end of file
+nuget pack Package.nuspec -outputDirectory "C:\Users\tor4k\local_nuget" -p version=0.3.2
\ No newline at end of file
diff --git a/Test/NiconicoToolkit.Shared.Test/VideoWatchTest.cs b/Test/NiconicoToolkit.Shared.Test/VideoWatchTest.cs
index 9ee5eaf..31bd259 100644
--- a/Test/NiconicoToolkit.Shared.Test/VideoWatchTest.cs
+++ b/Test/NiconicoToolkit.Shared.Test/VideoWatchTest.cs
@@ -177,9 +177,8 @@ public async Task NvGetCommentWithoutEasyPostAsync(string videoId)
var nvComment = res.WatchApiResponse.WatchApiData.Comment.NvComment;
var commentRes = await _context.Video.NvComment.GetCommentsAsync(
- nvComment.ThreadKey,
- nvComment.Params.Targets.Where(x => x.Fork != ThreadTargetForkConstants.Easy),
- nvComment.Params.Language
+ nvComment,
+ new[] { ThreadTargetForkConstants.Owner, ThreadTargetForkConstants.Main }
);
Assert.IsNotNull(commentRes.Data);
@@ -283,7 +282,7 @@ public async Task NvDeleteCommentAsync(string videoId)
var comment = res.WatchApiResponse.WatchApiData.Comment;
var nvComment = comment.NvComment;
- var commentRes = await _context.Video.NvComment.GetCommentsAsync(nvComment.ThreadKey, nvComment.Params.Targets.Where(x => x.Fork == ThreadTargetForkConstants.Easy), nvComment.Params.Language);
+ var commentRes = await _context.Video.NvComment.GetCommentsAsync(nvComment, new[] { ThreadTargetForkConstants.Easy });
var postedThread = commentRes.Data.Threads.FirstOrDefault(x => x.Fork == ThreadTargetForkConstants.Easy);
var myPostComments = postedThread.Comments.Where(x => x.IsMyPost);
int deleteTargetCommentNumber = -1;