From 2611836e5bddc6f0e87f5d5e49b0858a4e9e2ece Mon Sep 17 00:00:00 2001
From: Jdbye <jdbye3@gmail.com>
Date: Sun, 9 Jun 2024 18:00:59 +0200
Subject: [PATCH 1/5] Update README.md

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index 13ddaa0..ee58842 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,7 @@ Transmission-RPC-API
 [Official Transmission RPC specs](https://github.com/transmission/transmission/blob/master/extras/rpc-spec.txt) 
 
 C# implementation of the Transmission RPC API.
+Up to date with Transmission RPC specification as of Transmission 4.1.0 (rpc-version-semver 5.4.0, rpc-version: 18)
 
 | Command              | Not Implemented | Implemented|
 | -------------------- |:-:|:-:|
@@ -32,6 +33,9 @@ C# implementation of the Transmission RPC API.
 | queue-move-down      |   | x |
 | queue-move-bottom    |   | x |
 | free-space           |   | x |
+| group-set            |   | x |
+| group-get            |   | x |
+
 
 How to use
 -------------

From 7feb9e7e479c73db99d49f73789098b29ea484c6 Mon Sep 17 00:00:00 2001
From: Jdbye <jdbye3@gmail.com>
Date: Sun, 9 Jun 2024 18:12:56 +0200
Subject: [PATCH 2/5] Updated to latest RPC specifications as of Transmission
 4.1.0

* All new methods implemented
* All new fields readable
* "format" optional parameter of torrent-get remains unimplemented, for now. This is not needed but provides some efficiency benefits if the "table" format type is implemented.
---
 .../Arguments/BandwidthGroupSettings.cs       |  40 +
 Transmission.API.RPC/Arguments/NewTorrent.cs  |  21 +-
 .../Arguments/PortTestProtocol.cs             |  25 +
 .../Arguments/SessionFields.cs                | 139 ++++
 .../Arguments/SessionSettings.cs              |  74 +-
 .../Arguments/TorrentFields.cs                |  20 +-
 .../Arguments/TorrentSettings.cs              |  53 +-
 Transmission.API.RPC/Client.Async.cs          | 743 ++++++++++--------
 Transmission.API.RPC/Client.cs                |  90 ++-
 Transmission.API.RPC/Entity/BandwidthGroup.cs |  46 ++
 Transmission.API.RPC/Entity/FreeSpace.cs      |  33 +
 .../Entity/IntOrArrayConverter.cs             |  80 ++
 Transmission.API.RPC/Entity/NewTorrentInfo.cs |   5 +
 Transmission.API.RPC/Entity/SessionInfo.cs    | 104 ++-
 Transmission.API.RPC/Entity/Statistic.cs      |  20 +-
 Transmission.API.RPC/Entity/TorrentInfo.cs    | 268 ++++---
 Transmission.API.RPC/Entity/Units.cs          |   6 +-
 Transmission.API.RPC/ITransmissionClient.cs   |  47 +-
 .../ITransmissionClientAsync.cs               |  47 +-
 .../Transmission.API.RPC.csproj               |   2 +-
 20 files changed, 1310 insertions(+), 553 deletions(-)
 create mode 100644 Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs
 create mode 100644 Transmission.API.RPC/Arguments/PortTestProtocol.cs
 create mode 100644 Transmission.API.RPC/Arguments/SessionFields.cs
 create mode 100644 Transmission.API.RPC/Entity/BandwidthGroup.cs
 create mode 100644 Transmission.API.RPC/Entity/FreeSpace.cs
 create mode 100644 Transmission.API.RPC/Entity/IntOrArrayConverter.cs

diff --git a/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs b/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs
new file mode 100644
index 0000000..4d22b54
--- /dev/null
+++ b/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Transmission.API.RPC.Common;
+
+namespace Transmission.API.RPC.Arguments
+{
+    public class BandwidthGroupSettings : ArgumentsBase
+    {
+        /// <summary>
+        /// Session limits are honored
+        /// </summary>
+        public bool? HonorsSessionLimits { get { return GetValue<bool?>("honorsSessionLimits"); } set { this["honorsSessionLimits"] = value; } }
+
+        /// <summary>
+        /// Name of the bandwidth group
+        /// </summary>
+        public string Name { get { return GetValue<string>("name"); } set { this["name"] = value; } }
+
+        /// <summary>
+        /// Max global download speed of this bandwidth group (KBps)
+        /// </summary>
+        public long? SpeedLimitDown { get { return GetValue<long?>("speed-limit-down"); } set { this["speed-limit-down"] = value; } }
+
+        /// <summary>
+        /// True means enabled
+        /// </summary>
+        public bool? SpeedLimitDownEnabled { get { return GetValue<bool?>("speed-limit-down-enabled"); } set { this["speed-limit-down-enabled"] = value; } }
+
+        /// <summary>
+        /// Max global upload speed of this bandwidth group (KBps)
+        /// </summary>
+        public long? SpeedLimitUp { get { return GetValue<long?>("speed-limit-up"); } set { this["speed-limit-up"] = value; } }
+
+        /// <summary>
+        /// True means enabled
+        /// </summary>
+        public bool? SpeedLimitUpEnabled { get { return GetValue<bool?>("speed-limit-up-enabled"); } set { this["speed-limit-up-enabled"] = value; } }
+    }
+}
diff --git a/Transmission.API.RPC/Arguments/NewTorrent.cs b/Transmission.API.RPC/Arguments/NewTorrent.cs
index f367434..f3cd710 100644
--- a/Transmission.API.RPC/Arguments/NewTorrent.cs
+++ b/Transmission.API.RPC/Arguments/NewTorrent.cs
@@ -28,6 +28,11 @@ public class NewTorrent : ArgumentsBase
         /// </summary>
         public string Filename { get { return GetValue<string>("filename"); } set { this["filename"] = value; } }
 
+        /// <summary>
+        /// Array of labels applied to the torrent
+        /// </summary>
+        public string[] Labels { get { return GetValue<string[]>("labels"); } set { this["labels"] = value; } }
+
         /// <summary>
         /// base64-encoded .torrent content
         /// </summary>
@@ -36,41 +41,41 @@ public class NewTorrent : ArgumentsBase
         /// <summary>
         /// if true, don't start the torrent
         /// </summary>
-        public bool Paused { get { return GetValue<bool>("paused"); } set { this["paused"] = value; } }
+        public bool? Paused { get { return GetValue<bool>("paused"); } set { this["paused"] = value; } }
 
         /// <summary>
         /// maximum number of peers
         /// </summary>
-        public int? PeerLimit { get { return GetValue<int?>("peer-limit"); } set { this["peer-limit"] = value; } }
+        public long? PeerLimit { get { return GetValue<long?>("peer-limit"); } set { this["peer-limit"] = value; } }
 
         /// <summary>
         /// Torrent's bandwidth priority
         /// </summary>
-        public int? BandwidthPriority { get { return GetValue<int?>("bandwidthPriority"); } set { this["bandwidthPriority"] = value; } }
+        public long? BandwidthPriority { get { return GetValue<long?>("bandwidthPriority"); } set { this["bandwidthPriority"] = value; } }
 
         /// <summary>
         /// Indices of file(s) to download
         /// </summary>
-        public int[] FilesWanted { get { return GetValue<int[]>("files-wanted"); } set { this["files-wanted"] = value; } }
+        public long[] FilesWanted { get { return GetValue<long[]>("files-wanted"); } set { this["files-wanted"] = value; } }
 
         /// <summary>
         /// Indices of file(s) to download
         /// </summary>
-        public int[] FilesUnwanted { get { return GetValue<int[]>("files-unwanted"); } set { this["files-unwanted"] = value; } }
+        public long[] FilesUnwanted { get { return GetValue<long[]>("files-unwanted"); } set { this["files-unwanted"] = value; } }
 
         /// <summary>
         /// Indices of high-priority file(s)
         /// </summary>
-        public int[] PriorityHigh { get { return GetValue<int[]>("priority-high"); } set { this["priority-high"] = value; } }
+        public long[] PriorityHigh { get { return GetValue<long[]>("priority-high"); } set { this["priority-high"] = value; } }
 
         /// <summary>
         /// Indices of low-priority file(s)
         /// </summary>
-        public int[] PriorityLow { get { return GetValue<int[]>("priority-low"); } set { this["priority-low"] = value; } }
+        public long[] PriorityLow { get { return GetValue<long[]>("priority-low"); } set { this["priority-low"] = value; } }
 
         /// <summary>
         /// Indices of normal-priority file(s)
         /// </summary>
-        public int[] PriorityNormal { get { return GetValue<int[]>("priority-normal"); } set { this["priority-normal"] = value; } }
+        public long[] PriorityNormal { get { return GetValue<long[]>("priority-normal"); } set { this["priority-normal"] = value; } }
     }
 }
diff --git a/Transmission.API.RPC/Arguments/PortTestProtocol.cs b/Transmission.API.RPC/Arguments/PortTestProtocol.cs
new file mode 100644
index 0000000..d923140
--- /dev/null
+++ b/Transmission.API.RPC/Arguments/PortTestProtocol.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Transmission.API.RPC.Arguments
+{
+    /// <summary>
+    /// Which protocol was used for the port test
+    /// </summary>
+    public enum PortTestProtocol
+    {
+        /// <summary>
+        /// IPv4
+        /// </summary>
+        IPv4 = 0,
+        /// <summary>
+        /// IPv6
+        /// </summary>
+        IPV6,
+        /// <summary>
+        /// Could not be determined
+        /// </summary>
+        Unknown,
+    }
+}
diff --git a/Transmission.API.RPC/Arguments/SessionFields.cs b/Transmission.API.RPC/Arguments/SessionFields.cs
new file mode 100644
index 0000000..b0855e8
--- /dev/null
+++ b/Transmission.API.RPC/Arguments/SessionFields.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Transmission.API.RPC.Arguments
+{
+    /// <summary>
+    /// Session fields
+    /// </summary>
+    public sealed class SessionFields
+    {
+        private SessionFields() { }
+
+        public const string ALT_SPEED_DOWN = "alt-speed-down";
+        public const string ALT_SPEED_ENABLED = "alt-speed-enabled";
+        public const string ALT_SPEED_TIME_BEGIN = "alt-speed-time-begin";
+        public const string ALT_SPEED_TIME_ENABLED = "alt-speed-time-enabled";
+        public const string ALT_SPEED_TIME_END = "alt-speed-time-end";
+        public const string ALT_SPEED_TIME_DAY = "alt-speed-time-day";
+        public const string ALT_SPEED_UP = "alt-speed-up";
+        public const string BLOCKLIST_URL = "blocklist-url";
+        public const string BLOCKLIST_ENABLED = "blocklist-enabled";
+        public const string BLOCKLIST_SIZE = "blocklist-size";
+        public const string CACHE_SIZE_MB = "cache-size-mb";
+        public const string DEFAULT_TRACKERS = "default-trackers";
+        public const string DHT_ENABLED = "dht-enabled";
+        public const string DOWNLOAD_DIR = "download-dir";
+        public const string DOWNLOAD_DIR_FREE_SPACE = "download-dir-free-space";
+        public const string DOWNLOAD_QUEUE_SIZE = "download-queue-size";
+        public const string DOWNLOAD_QUEUE_ENABLED = "download-queue-enabled";
+        public const string ENCRYPTION = "encryption";
+        public const string IDLE_SEEDING_LIMIT = "idle-seeding-limit";
+        public const string IDLE_SEEDING_LIMIT_ENABLED = "idle-seeding-limit-enabled";
+        public const string INCOMPLETE_DIR = "incomplete-dir";
+        public const string INCOMPLETE_DIR_ENABLED = "incomplete-dir-enabled";
+        public const string LPD_ENABLED = "lpd-enabled";
+        public const string PEER_LIMIT_GLOBAL = "peer-limit-global";
+        public const string PEER_LIMIT_PER_TORRENT = "peer-limit-per-torrent";
+        public const string PEX_ENABLED = "pex-enabled";
+        public const string PEER_PORT = "peer-port";
+        public const string PEER_PORT_RANDOM_ON_START = "peer-port-random-on-start";
+        public const string PORT_FORWARDING_ENABLED = "port-forwarding-enabled";
+        public const string QUEUE_STALLED_ENABLED = "queue-stalled-enabled";
+        public const string QUEUE_STALLED_MINUTES = "queue-stalled-minutes";
+        public const string RENAME_PARTIAL_FILES = "rename-partial-files";
+        public const string SESSION_ID = "session-id";
+        public const string SCRIPT_TORRENT_ADDED_FILENAME = "script-torrent-added-filename";
+        public const string SCRIPT_TORRENT_ADDED_ENABLED = "script-torrent-added-enabled";
+        public const string SCRIPT_TORRENT_DONE_FILENAME = "script-torrent-done-filename";
+        public const string SCRIPT_TORRENT_DONE_ENABLED = "script-torrent-done-enabled";
+        public const string SCRIPT_TORRENT_DONE_SEEDING_FILENAME = "script-torrent-done-seeding-filename";
+        public const string SCRIPT_TORRENT_DONE_SEEDING_ENABLED = "script-torrent-done-seeding-enabled";
+        public const string SEED_RATIO_LIMIT = "seedRatioLimit";
+        public const string SEED_RATIO_LIMITED = "seedRatioLimited";
+        public const string SEED_QUEUE_SIZE = "seed-queue-size";
+        public const string SEED_QUEUE_ENABLED = "seed-queue-enabled";
+        public const string SPEED_LIMIT_DOWN = "speed-limit-down";
+        public const string SPEED_LIMIT_DOWN_ENABLED = "speed-limit-down-enabled";
+        public const string SPEED_LIMIT_UP = "speed-limit-up";
+        public const string SPEED_LIMIT_UP_ENABLED = "speed-limit-up-enabled";
+        public const string START_ADDED_TORRENTS = "start-added-torrents";
+        public const string TRASH_ORIGINAL_TORRENT_FILES = "trash-original-torrent-files";
+        public const string UNITS = "units";
+        public const string UTP_ENABLED = "utp-enabled";
+        public const string CONFIG_DIR = "config-dir";
+        public const string RPC_VERSION = "rpc-version";
+        public const string RPC_VERSION_MINIMUM = "rpc-version-minimum";
+        public const string RPC_VERSION_SEMVER = "rpc-version-semver";
+        public const string VERSION = "version";
+
+        public static string[] ALL_FIELDS
+        {
+            get
+            {
+                return new string[]
+                {
+                    #region ALL FIELDS
+                    ALT_SPEED_DOWN,
+                    ALT_SPEED_ENABLED,
+                    ALT_SPEED_TIME_BEGIN,
+                    ALT_SPEED_TIME_ENABLED,
+                    ALT_SPEED_TIME_END,
+                    ALT_SPEED_TIME_DAY,
+                    ALT_SPEED_UP,
+                    BLOCKLIST_URL,
+                    BLOCKLIST_ENABLED,
+                    BLOCKLIST_SIZE,
+                    CACHE_SIZE_MB,
+                    DEFAULT_TRACKERS,
+                    DHT_ENABLED,
+                    DOWNLOAD_DIR,
+                    DOWNLOAD_DIR_FREE_SPACE,
+                    DOWNLOAD_QUEUE_SIZE,
+                    DOWNLOAD_QUEUE_ENABLED,
+                    ENCRYPTION,
+                    IDLE_SEEDING_LIMIT,
+                    IDLE_SEEDING_LIMIT_ENABLED,
+                    INCOMPLETE_DIR,
+                    INCOMPLETE_DIR_ENABLED,
+                    LPD_ENABLED,
+                    PEER_LIMIT_GLOBAL,
+                    PEER_LIMIT_PER_TORRENT,
+                    PEX_ENABLED,
+                    PEER_PORT,
+                    PEER_PORT_RANDOM_ON_START,
+                    PORT_FORWARDING_ENABLED,
+                    QUEUE_STALLED_ENABLED,
+                    QUEUE_STALLED_MINUTES,
+                    RENAME_PARTIAL_FILES,
+                    SESSION_ID,
+                    SCRIPT_TORRENT_ADDED_FILENAME,
+                    SCRIPT_TORRENT_ADDED_ENABLED,
+                    SCRIPT_TORRENT_DONE_FILENAME,
+                    SCRIPT_TORRENT_DONE_ENABLED,
+                    SCRIPT_TORRENT_DONE_SEEDING_FILENAME,
+                    SCRIPT_TORRENT_DONE_SEEDING_ENABLED,
+                    SEED_RATIO_LIMIT,
+                    SEED_RATIO_LIMITED,
+                    SEED_QUEUE_SIZE,
+                    SEED_QUEUE_ENABLED,
+                    SPEED_LIMIT_DOWN,
+                    SPEED_LIMIT_DOWN_ENABLED,
+                    SPEED_LIMIT_UP,
+                    SPEED_LIMIT_UP_ENABLED,
+                    START_ADDED_TORRENTS,
+                    TRASH_ORIGINAL_TORRENT_FILES,
+                    UNITS,
+                    UTP_ENABLED,
+                    CONFIG_DIR,
+                    RPC_VERSION,
+                    RPC_VERSION_MINIMUM,
+                    RPC_VERSION_SEMVER,
+                    VERSION,
+                    #endregion
+                };
+            }
+        }
+    }
+}
diff --git a/Transmission.API.RPC/Arguments/SessionSettings.cs b/Transmission.API.RPC/Arguments/SessionSettings.cs
index 33bec06..d442905 100644
--- a/Transmission.API.RPC/Arguments/SessionSettings.cs
+++ b/Transmission.API.RPC/Arguments/SessionSettings.cs
@@ -17,7 +17,7 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Max global download speed (KBps)
         /// </summary>
-        public int? AlternativeSpeedDown { get { return GetValue<int?>("alt-speed-down"); } set { this["alt-speed-down"] = value; } }
+        public long? AlternativeSpeedDown { get { return GetValue<long?>("alt-speed-down"); } set { this["alt-speed-down"] = value; } }
 
         /// <summary>
         /// True means use the alt speeds
@@ -27,7 +27,7 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// When to turn on alt speeds (units: minutes after midnight)
         /// </summary>
-        public int? AlternativeSpeedTimeBegin { get { return GetValue<int?>("alt-speed-time-begin"); } set { this["alt-speed-time-begin"] = value; } }
+        public long? AlternativeSpeedTimeBegin { get { return GetValue<long?>("alt-speed-time-begin"); } set { this["alt-speed-time-begin"] = value; } }
 
         /// <summary>
         /// True means the scheduled on/off times are used
@@ -37,22 +37,22 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// When to turn off alt speeds
         /// </summary>
-        public int? AlternativeSpeedTimeEnd { get { return GetValue<int?>("alt-speed-time-end"); } set { this["alt-speed-time-end"] = value; } }
+        public long? AlternativeSpeedTimeEnd { get { return GetValue<long?>("alt-speed-time-end"); } set { this["alt-speed-time-end"] = value; } }
 
         /// <summary>
         /// What day(s) to turn on alt speeds
         /// </summary>
-        public int? AlternativeSpeedTimeDay { get { return GetValue<int?>("alt-speed-time-day"); } set { this["alt-speed-time-day"] = value; } }
+        public long? AlternativeSpeedTimeDay { get { return GetValue<long?>("alt-speed-time-day"); } set { this["alt-speed-time-day"] = value; } }
 
         /// <summary>
         /// Max global upload speed (KBps)
         /// </summary>
-        public int? AlternativeSpeedUp { get { return GetValue<int?>("alt-speed-up"); } set { this["alt-speed-up"] = value; } }
+        public long? AlternativeSpeedUp { get { return GetValue<long?>("alt-speed-up"); } set { this["alt-speed-up"] = value; } }
 
         /// <summary>
         /// Location of the blocklist to use for "blocklist-update"
         /// </summary>
-        public string BlocklistURL { get { return GetValue<string>("blocklist-url"); } set { this["blocklist-url"] = value; } }
+        public string BlocklistUrl { get { return GetValue<string>("blocklist-url"); } set { this["blocklist-url"] = value; } }
 
         /// <summary>
         /// True means enabled
@@ -62,17 +62,22 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Maximum size of the disk cache (MB)
         /// </summary>
-        public int? CacheSizeMB { get { return GetValue<int?>("cache-size-mb"); } set { this["cache-size-mb"] = value; } }
+        public long? CacheSizeMb { get { return GetValue<long?>("cache-size-mb"); } set { this["cache-size-mb"] = value; } }
 
         /// <summary>
-        /// Default path to download torrents
+        /// Announce URLs, one per line, and a blank line between tiers
+        /// </summary>
+        public string DefaultTrackers { get { return GetValue<string>("default-trackers"); } set { this["default-trackers"] = value; } }
+
+        /// <summary>
+        /// Default path to download torrents to
         /// </summary>
         public string DownloadDirectory { get { return GetValue<string>("download-dir"); } set { this["download-dir"] = value; } }
 
         /// <summary>
         /// Max number of torrents to download at once (see download-queue-enabled)
         /// </summary>
-        public int? DownloadQueueSize { get { return GetValue<int?>("download-queue-size"); } set { this["download-queue-size"] = value; } }
+        public long? DownloadQueueSize { get { return GetValue<long?>("download-queue-size"); } set { this["download-queue-size"] = value; } }
 
         /// <summary>
         /// If true, limit how many torrents can be downloaded at once
@@ -82,7 +87,7 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// True means allow dht in public torrents
         /// </summary>
-        public bool? DHTEnabled { get { return GetValue<bool?>("dht-enabled"); } set { this["dht-enabled"] = value; } }
+        public bool? DhtEnabled { get { return GetValue<bool?>("dht-enabled"); } set { this["dht-enabled"] = value; } }
 
         /// <summary>
         /// "required", "preferred", "tolerated"
@@ -90,9 +95,9 @@ public class SessionSettings : ArgumentsBase
         public string Encryption { get { return GetValue<string>("encryption"); } set { this["encryption"] = value; } }
 
         /// <summary>
-        /// Torrents we're seeding will be stopped if they're idle for this long
+        /// Torrents we're seeding will be stopped if they're idle for this long?
         /// </summary>
-        public int? IdleSeedingLimit { get { return GetValue<int?>("idle-seeding-limit"); } set { this["idle-seeding-limit"] = value; } }
+        public long? IdleSeedingLimit { get { return GetValue<long?>("idle-seeding-limit"); } set { this["idle-seeding-limit"] = value; } }
 
         /// <summary>
         /// True if the seeding inactivity limit is honored by default
@@ -112,17 +117,17 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// True means allow Local Peer Discovery in public torrents
         /// </summary>
-        public bool? LPDEnabled { get { return GetValue<bool?>("lpd-enabled"); } set { this["lpd-enabled"] = value; } }
+        public bool? LpdEnabled { get { return GetValue<bool?>("lpd-enabled"); } set { this["lpd-enabled"] = value; } }
 
         /// <summary>
         /// Maximum global number of peers
         /// </summary>
-        public int? PeerLimitGlobal { get { return GetValue<int?>("peer-limit-global"); } set { this["peer-limit-global"] = value; } }
+        public long? PeerLimitGlobal { get { return GetValue<long?>("peer-limit-global"); } set { this["peer-limit-global"] = value; } }
 
         /// <summary>
         /// Maximum global number of peers
         /// </summary>
-        public int? PeerLimitPerTorrent { get { return GetValue<int?>("peer-limit-per-torrent"); } set { this["peer-limit-per-torrent"] = value; } }
+        public long? PeerLimitPerTorrent { get { return GetValue<long?>("peer-limit-per-torrent"); } set { this["peer-limit-per-torrent"] = value; } }
 
         /// <summary>
         /// True means allow pex in public torrents
@@ -132,7 +137,7 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Port number
         /// </summary>
-        public int? PeerPort { get { return GetValue<int?>("peer-port"); } set { this["peer-port"] = value; } }
+        public long? PeerPort { get { return GetValue<long?>("peer-port"); } set { this["peer-port"] = value; } }
 
         /// <summary>
         /// True means pick a random peer port on launch
@@ -152,13 +157,23 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Torrents that are idle for N minuets aren't counted toward seed-queue-size or download-queue-size
         /// </summary>
-        public int? QueueStalledMinutes { get { return GetValue<int?>("queue-stalled-minutes"); } set { this["queue-stalled-minutes"] = value; } }
+        public long? QueueStalledMinutes { get { return GetValue<long?>("queue-stalled-minutes"); } set { this["queue-stalled-minutes"] = value; } }
 
         /// <summary>
         /// True means append ".part" to incomplete files
         /// </summary>
         public bool? RenamePartialFiles { get { return GetValue<bool?>("rename-partial-files"); } set { this["rename-partial-files"] = value; } }
 
+        /// <summary>
+        /// Filename of the script to run
+        /// </summary>
+        public string ScriptTorrentAddedFilename { get { return GetValue<string>("script-torrent-added-filename"); } set { this["script-torrent-added-filename"] = value; } }
+
+        /// <summary>
+        /// Whether or not to call the "added" script
+        /// </summary>
+        public bool? ScriptTorrentAddedEnabled { get { return GetValue<bool?>("script-torrent-added-enabled"); } set { this["script-torrent-added-enabled"] = value; } }
+
         /// <summary>
         /// Filename of the script to run
         /// </summary>
@@ -169,10 +184,20 @@ public class SessionSettings : ArgumentsBase
         /// </summary>
         public bool? ScriptTorrentDoneEnabled { get { return GetValue<bool?>("script-torrent-done-enabled"); } set { this["script-torrent-done-enabled"] = value; } }
 
+        /// <summary>
+        /// Filename of the script to run
+        /// </summary>
+        public string ScriptTorrentDoneSeedingFilename { get { return GetValue<string>("script-torrent-done-seeding-filename"); } set { this["script-torrent-done-seeding-filename"] = value; } }
+
+        /// <summary>
+        /// Whether or not to call the "done seeding" script
+        /// </summary>
+        public bool? ScriptTorrentDoneSeedingEnabled { get { return GetValue<bool?>("script-torrent-done-seeding-enabled"); } set { this["script-torrent-done-seeding-enabled"] = value; } }
+
         /// <summary>
         /// The default seed ratio for torrents to use
         /// </summary>
-        public double? SeedRatioLimit { get { return GetValue<int?>("seedRatioLimit"); } set { this["seedRatioLimit"] = value; } }
+        public double? SeedRatioLimit { get { return GetValue<long?>("seedRatioLimit"); } set { this["seedRatioLimit"] = value; } }
 
         /// <summary>
         /// True if seedRatioLimit is honored by default
@@ -182,7 +207,7 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Max number of torrents to uploaded at once (see seed-queue-enabled)
         /// </summary>
-        public int? SeedQueueSize { get { return GetValue<int?>("seed-queue-size"); } set { this["seed-queue-size"] = value; } }
+        public long? SeedQueueSize { get { return GetValue<long?>("seed-queue-size"); } set { this["seed-queue-size"] = value; } }
 
         /// <summary>
         /// If true, limit how many torrents can be uploaded at once
@@ -192,7 +217,7 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Max global download speed (KBps)
         /// </summary>
-        public int? SpeedLimitDown { get { return GetValue<int?>("speed-limit-down"); } set { this["speed-limit-down"] = value; } }
+        public long? SpeedLimitDown { get { return GetValue<long?>("speed-limit-down"); } set { this["speed-limit-down"] = value; } }
 
         /// <summary>
         /// True means enabled
@@ -202,7 +227,7 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         ///  max global upload speed (KBps)
         /// </summary>
-        public int? SpeedLimitUp { get { return GetValue<int?>("speed-limit-up"); } set { this["speed-limit-up"] = value; } }
+        public long? SpeedLimitUp { get { return GetValue<long?>("speed-limit-up"); } set { this["speed-limit-up"] = value; } }
 
         /// <summary>
         /// True means enabled
@@ -219,15 +244,10 @@ public class SessionSettings : ArgumentsBase
         /// </summary>
         public bool? TrashOriginalTorrentFiles { get { return GetValue<bool?>("trash-original-torrent-files"); } set { this["trash-original-torrent-files"] = value; } }
 
-        /// <summary>
-        /// Units
-        /// </summary>
-        public Units Units { get { return GetValue<Units>("units"); } set { this["units"] = value; } }
-
         /// <summary>
         /// True means allow utp
         /// </summary>
-        public bool? UtpEnabled { get { return GetValue<bool?> ("utp-enabled"); } set { this["utp-enabled"] = value; } }
+        public bool? UtpEnabled { get { return GetValue<bool?>("utp-enabled"); } set { this["utp-enabled"] = value; } }
 
     }
 }
diff --git a/Transmission.API.RPC/Arguments/TorrentFields.cs b/Transmission.API.RPC/Arguments/TorrentFields.cs
index dd69edd..a04c856 100644
--- a/Transmission.API.RPC/Arguments/TorrentFields.cs
+++ b/Transmission.API.RPC/Arguments/TorrentFields.cs
@@ -23,6 +23,11 @@ private TorrentFields() { }
         /// </summary>
         public const string ADDED_DATE = "addedDate";
 
+        /// <summary>
+        /// availability
+        /// </summary>
+        public const string AVAILABILITY = "availability";
+
         /// <summary>
         /// bandwidthPriority
         /// </summary>
@@ -82,7 +87,7 @@ private TorrentFields() { }
         /// editDate
         /// </summary>
         public const string EDIT_DATE = "editDate";
-
+        
         /// <summary>
         /// error
         /// </summary>
@@ -118,6 +123,11 @@ private TorrentFields() { }
         /// </summary>
         public const string FILE_STATS = "fileStats";
 
+        /// <summary>
+        /// group
+        /// </summary>
+        public const string GROUP = "group";
+
         /// <summary>
         /// hashString
         /// </summary>
@@ -308,6 +318,11 @@ private TorrentFields() { }
         /// </summary>
         public const string SEED_RATIO_MODE = "seedRatioMode";
 
+        /// <summary>
+        /// sequentialDownload
+        /// </summary>
+        public const string SEQUENTIAL_DOWNLOAD = "sequentialDownload";
+
         /// <summary>
         /// sizeWhenDone
         /// </summary>
@@ -395,6 +410,7 @@ public static string[] ALL_FIELDS
                     #region ALL FIELDS
                     ACTIVITY_DATE,
                     ADDED_DATE,
+                    AVAILABILITY,
                     BANDWIDTH_PRIORITY,
                     COMMENT,
                     CORRUPT_EVER,
@@ -414,6 +430,7 @@ public static string[] ALL_FIELDS
                     FILE_COUNT,
                     FILES,
                     FILE_STATS,
+                    GROUP,
                     HASH_STRING,
                     HAVE_UNCHECKED,
                     HAVE_VALID,
@@ -452,6 +469,7 @@ public static string[] ALL_FIELDS
                     SEED_IDLE_MODE,
                     SEED_RATIO_LIMIT,
                     SEED_RATIO_MODE,
+                    SEQUENTIAL_DOWNLOAD,
                     SIZE_WHEN_DONE,
                     START_DATE,
                     STATUS,
diff --git a/Transmission.API.RPC/Arguments/TorrentSettings.cs b/Transmission.API.RPC/Arguments/TorrentSettings.cs
index 5b1c8a3..b0e1276 100644
--- a/Transmission.API.RPC/Arguments/TorrentSettings.cs
+++ b/Transmission.API.RPC/Arguments/TorrentSettings.cs
@@ -1,6 +1,7 @@
 using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
+using System.Drawing;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -16,18 +17,23 @@ public class TorrentSettings : ArgumentsBase
         /// <summary>
         /// This torrent's bandwidth tr_priority_t
         /// </summary>
-        public int? BandwidthPriority { get { return GetValue<int?>("bandwidthPriority"); } set { this["bandwidthPriority"] = value; } }
+        public long? BandwidthPriority { get { return GetValue<long?>("bandwidthPriority"); } set { this["bandwidthPriority"] = value; } }
 
         /// <summary>
         /// Maximum download speed (KBps)
         /// </summary>
-        public int? DownloadLimit { get { return GetValue<int?>("downloadLimit"); } set { this["downloadLimit"] = value; } }
+        public long? DownloadLimit { get { return GetValue<long?>("downloadLimit"); } set { this["downloadLimit"] = value; } }
 
         /// <summary>
         /// Download limit is honored
         /// </summary>
         public bool? DownloadLimited { get { return GetValue<bool?>("downloadLimited"); } set { this["downloadLimited"] = value; } }
 
+        /// <summary>
+        /// The name of this torrent's bandwidth group
+        /// </summary>
+        public string Group { get { return GetValue<string>("group"); } set { this["group"] = value; } }
+
         /// <summary>
         /// Session upload limits are honored
         /// </summary>
@@ -36,7 +42,12 @@ public class TorrentSettings : ArgumentsBase
         /// <summary>
         /// Torrent id array
         /// </summary>
-        public object[] IDs { get { return GetValue<object[]>("ids"); } set { this["ids"] = value; } }
+        public object[] Ids { get { return GetValue<object[]>("ids"); } set { this["ids"] = value; } }
+
+        /// <summary>
+        /// Array of strings containing the labels assigned to the torrent
+        /// </summary>
+        public string[] Labels { get { return GetValue<string[]>("labels"); } set { this["labels"] = value; } }
 
         /// <summary>
         /// New location of the torrent's content
@@ -46,22 +57,22 @@ public class TorrentSettings : ArgumentsBase
         /// <summary>
         /// Maximum number of peers
         /// </summary>
-        public int? PeerLimit { get { return GetValue<int?>("peer-limit"); } set { this["peer-limit"] = value; } }
+        public long? PeerLimit { get { return GetValue<long?>("peer-limit"); } set { this["peer-limit"] = value; } }
 
         /// <summary>
         /// Position of this torrent in its queue [0...n)
         /// </summary>
-        public int? QueuePosition { get { return GetValue<int?>("queuePosition"); } set { this["queuePosition"] = value; } }
+        public long? QueuePosition { get { return GetValue<long?>("queuePosition"); } set { this["queuePosition"] = value; } }
 
         /// <summary>
         /// Torrent-level number of minutes of seeding inactivity
         /// </summary>
-        public int? SeedIdleLimit { get { return GetValue<int?>("seedIdleLimit"); } set { this["seedIdleLimit"] = value; } }
+        public long? SeedIdleLimit { get { return GetValue<long?>("seedIdleLimit"); } set { this["seedIdleLimit"] = value; } }
 
         /// <summary>
-        /// Which seeding inactivity to use
+        /// Which seeding inactivity mode to use. 0=Global 1=Single 2=Unlimited
         /// </summary>
-        public int? SeedIdleMode { get { return GetValue<int?>("seedIdleMode"); } set { this["seedIdleMode"] = value; } }
+        public long? SeedIdleMode { get { return GetValue<long?>("seedIdleMode"); } set { this["seedIdleMode"] = value; } }
 
         /// <summary>
         /// Torrent-level seeding ratio
@@ -69,14 +80,19 @@ public class TorrentSettings : ArgumentsBase
         public double? SeedRatioLimit { get { return GetValue<double?>("seedRatioLimit"); } set { this["seedRatioLimit"] = value; } }
 
         /// <summary>
-        /// Which ratio to use. 
+        /// Which ratio mode to use. 0=Global 1=Single 2=Unlimited
+        /// </summary>
+        public long? SeedRatioMode { get { return GetValue<long?>("seedRatioMode"); } set { this["seedRatioMode"] = value; } }
+
+        /// <summary>
+        /// Whether to download the torrent sequentially
         /// </summary>
-        public int? SeedRatioMode { get { return GetValue<int?>("seedRatioMode"); } set { this["seedRatioMode"] = value; } }
+        public bool? SequentialDownload { get { return GetValue<bool?>("sequentialDownload"); } set { this["sequentialDownload"] = value; } }
 
         /// <summary>
         /// Maximum upload speed (KBps)
         /// </summary>
-        public int? UploadLimit { get { return GetValue<int?>("uploadLimit"); } set { this["uploadLimit"] = value; } }
+        public long? UploadLimit { get { return GetValue<long?>("uploadLimit"); } set { this["uploadLimit"] = value; } }
 
         /// <summary>
         /// Upload limit is honored
@@ -93,12 +109,18 @@ public class TorrentSettings : ArgumentsBase
         /// Ids of trackers to remove
         /// </summary>
         [Obsolete("TrackerRemove is obsolete since Transmission 4.0.0, use TrackerList instead.")]
-		public int[] TrackerRemove { get { return GetValue<int[]>("trackerRemove"); } set { this["trackerRemove"] = value; } }
+		public long?[] TrackerRemove { get { return GetValue<long?[]>("trackerRemove"); } set { this["trackerRemove"] = value; } }
+
+        /// <summary>
+        /// Pairs of IDs of announce URLs to replace along with their new value
+        /// </summary>
+        [Obsolete("TrackerReplace is obsolete since Transmission 4.0.0, use TrackerList instead.")]
+        public KeyValuePair<int, string>?[] TrackerReplace { get { return GetValue<KeyValuePair<int, string>?[]>("trackerReplace"); } set { this["trackerReplace"] = value; } }
 
         /// <summary>
         /// String of announce URLs, one per line, with a blank line between tiers
         /// </summary>
-        public string[] TrackerList { get { return GetValue<string[]>("trackerList"); } set { this["trackerAdd"] = value; } }
+        public string[] TrackerList { get { return GetValue<string[]>("trackerList"); } set { this["trackerList"] = value; } }
 
 		/// <summary>
 		/// Files wanted
@@ -124,10 +146,5 @@ public class TorrentSettings : ArgumentsBase
 		/// Normal priority files
 		/// </summary>
 		public string[] PriorityNormal { get { return GetValue<string[]>("priority-normal"); } set { this["priority-normal"] = value; } }
-
-		//TODO: Add and test
-		//"trackerReplace"      | array      pairs of <trackerId/new announce URLs>
-		//public [] trackerReplace;
-
 	}
 }
diff --git a/Transmission.API.RPC/Client.Async.cs b/Transmission.API.RPC/Client.Async.cs
index f1c96c8..14aba55 100644
--- a/Transmission.API.RPC/Client.Async.cs
+++ b/Transmission.API.RPC/Client.Async.cs
@@ -11,119 +11,142 @@
 using Newtonsoft.Json.Linq;
 using Transmission.API.RPC.Common;
 using Transmission.API.RPC.Arguments;
+using System.Runtime;
 
 namespace Transmission.API.RPC
 {
-	public partial class Client
-	{
-		#region Session methods
-
-		/// <summary>
-		/// Close current session (API: session-close)
-		/// </summary>
-		public async Task CloseSessionAsync()
-		{
-			var request = new TransmissionRequest("session-close");
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Set information to current session (API: session-set)
-		/// </summary>
-		/// <param name="settings">New session settings</param>
-		public async Task SetSessionSettingsAsync(SessionSettings settings)
-		{
-			var request = new TransmissionRequest("session-set", settings);
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Get session stat
-		/// </summary>
-		/// <returns>Session stat</returns>
-		public async Task<Statistic> GetSessionStatisticAsync()
-		{
-			var request = new TransmissionRequest("session-stats");
-			var response = await SendRequestAsync(request);
-			var result = response.Deserialize<Statistic>();
-			return result;
-		}
+    public partial class Client
+    {
+        #region Session methods
+
+        /// <summary>
+        /// Close current session (API: session-close)
+        /// </summary>
+        public async Task CloseSessionAsync()
+        {
+            var request = new TransmissionRequest("session-close");
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Set information to current session (API: session-set)
+        /// </summary>
+        /// <param name="settings">New session settings</param>
+        public async Task SetSessionSettingsAsync(SessionSettings settings)
+        {
+            var request = new TransmissionRequest("session-set", settings);
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Get session stat
+        /// </summary>
+        /// <returns>Session stat</returns>
+        public async Task<Statistic> GetSessionStatisticAsync()
+        {
+            var request = new TransmissionRequest("session-stats");
+            var response = await SendRequestAsync(request);
+            var result = response.Deserialize<Statistic>();
+            return result;
+        }
 
         /// <summary>
         /// Get information of current session (API: session-get)
         /// </summary>
         /// <returns>Session information</returns>
-        //TODO: support optional "fields" argument
         public async Task<SessionInfo> GetSessionInformationAsync()
-		{
-			var request = new TransmissionRequest("session-get");
-			var response = await SendRequestAsync(request);
-			var result = response.Deserialize<SessionInfo>();
-			return result;
-		}
+        {
+            var request = new TransmissionRequest("session-get");
+            var response = await SendRequestAsync(request);
+            var result = response.Deserialize<SessionInfo>();
+            return result;
+        }
+
+
+
+        /// <summary>
+        /// Get information of current session (API: session-get)
+        /// </summary>
+        /// <param name="fields">Optional fields of session information</param>
+        /// <returns>Session information</returns>
+        public async Task<SessionInfo> GetSessionInformationAsync(string[] fields)
+        {
+            var arguments = new Dictionary<string, object>();
+            arguments.Add("fields", fields);
 
-		#endregion
+            var request = new TransmissionRequest("session-get", arguments);
+            var response = await SendRequestAsync(request);
+            var result = response.Deserialize<SessionInfo>();
+            return result;
+        }
+        #endregion
 
-		#region Torrents methods
+        #region Torrents methods
 
-		/// <summary>
-		/// Add torrent (API: torrent-add)
-		/// </summary>
-		/// <returns>Torrent info (ID, Name and HashString)</returns>
-		public async Task<NewTorrentInfo> TorrentAddAsync(NewTorrent torrent)
-		{
-			if (String.IsNullOrWhiteSpace(torrent.Metainfo) && String.IsNullOrWhiteSpace(torrent.Filename))
-				throw new Exception("Either \"filename\" or \"metainfo\" must be included.");
+        /// <summary>
+        /// Add torrent (API: torrent-add)
+        /// </summary>
+        /// <returns>Torrent info (ID, Name and HashString)</returns>
+        public async Task<NewTorrentInfo> TorrentAddAsync(NewTorrent torrent)
+        {
+            if (String.IsNullOrWhiteSpace(torrent.Metainfo) && String.IsNullOrWhiteSpace(torrent.Filename))
+                throw new Exception("Either \"filename\" or \"metainfo\" must be included.");
 
-			var request = new TransmissionRequest("torrent-add", torrent);
-			var response = await SendRequestAsync(request);
-			var jObject = response.Deserialize<JObject>();
+            var request = new TransmissionRequest("torrent-add", torrent);
+            var response = await SendRequestAsync(request);
+            var jObject = response.Deserialize<JObject>();
 
-			if (jObject == null || jObject.First == null)
-				return null;
+            if (jObject == null || jObject.First == null)
+                return null;
 
-			NewTorrentInfo result = null;
-			JToken value = null;
+            NewTorrentInfo result = null;
+            JToken value = null;
 
-			if (jObject.TryGetValue("torrent-duplicate", out value))
-				result = JsonConvert.DeserializeObject<NewTorrentInfo>(value.ToString());
-			else if (jObject.TryGetValue("torrent-added", out value))
-				result = JsonConvert.DeserializeObject<NewTorrentInfo>(value.ToString());
+            if (jObject.TryGetValue("torrent-duplicate", out value))
+            {
+                result = JsonConvert.DeserializeObject<NewTorrentInfo>(value.ToString());
+                result.Duplicate = true;
+            }
+            else if (jObject.TryGetValue("torrent-added", out value))
+            {
+                result = JsonConvert.DeserializeObject<NewTorrentInfo>(value.ToString());
+                result.Duplicate = false;
+            }
 
-			return result;
-		}
+            return result;
+        }
 
         /// <summary>
         /// Set torrent params (API: torrent-set)
         /// </summary>
         /// <param name="settings">Torrent settings</param>
         public async Task TorrentSetAsync(TorrentSettings settings)
-		{
-			var request = new TransmissionRequest("torrent-set", settings);
-			var response = await SendRequestAsync(request);
-		}
+        {
+            var request = new TransmissionRequest("torrent-set", settings);
+            var response = await SendRequestAsync(request);
+        }
 
-		/// <summary>
-		/// Get fields of torrents from ids (API: torrent-get)
-		/// </summary>
-		/// <param name="fields">Fields of torrents</param>
-		/// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
-		/// <returns>Torrents info</returns>
-		public async Task<TransmissionTorrents> TorrentGetAsync(string[] fields, params int[] ids)
-		{
-			var arguments = new Dictionary<string, object>();
-			arguments.Add("fields", fields);
+        /// <summary>
+        /// Get fields of torrents from ids (API: torrent-get)
+        /// </summary>
+        /// <param name="fields">Fields of torrents</param>
+        /// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
+        /// <returns>Torrents info</returns>
+        public async Task<TransmissionTorrents> TorrentGetAsync(string[] fields, params int[] ids)
+        {
+            var arguments = new Dictionary<string, object>();
+            arguments.Add("fields", fields);
 
-			if (ids != null && ids.Length > 0)
-				arguments.Add("ids", ids);
+            if (ids != null && ids.Length > 0)
+                arguments.Add("ids", ids);
 
-			var request = new TransmissionRequest("torrent-get", arguments);
+            var request = new TransmissionRequest("torrent-get", arguments);
 
-			var response = await SendRequestAsync(request);
-			var result = response.Deserialize<TransmissionTorrents>();
+            var response = await SendRequestAsync(request);
+            var json = response.ToJson
 
-			return result;
-		}
+            return result;
+        }
 
         /// <summary>
         /// Remove torrents
@@ -131,247 +154,315 @@ public async Task<TransmissionTorrents> TorrentGetAsync(string[] fields, params
         /// <param name="ids">Torrents id</param>
         /// <param name="deleteData">Remove data</param>
         public async Task TorrentRemoveAsync(int[] ids, bool deleteData = false)
-		{
-			var arguments = new Dictionary<string, object>();
-
-			arguments.Add("ids", ids);
-			arguments.Add("delete-local-data", deleteData);
-
-			var request = new TransmissionRequest("torrent-remove", arguments);
-			var response = await SendRequestAsync(request);
-		}
-
-		#region Torrent Start
-
-		/// <summary>
-		/// Start torrents (API: torrent-start)
-		/// </summary>
-		/// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
-		public async Task TorrentStartAsync(object[] ids)
-		{
-			var request = new TransmissionRequest("torrent-start", new Dictionary<string, object> { { "ids", ids } });
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Start recently active torrents (API: torrent-start)
-		/// </summary>
-		public async Task TorrentStartAsync()
-		{
-			var request = new TransmissionRequest("torrent-start", new Dictionary<string, object> { { "ids", "recently-active" } });
-			var response = await SendRequestAsync(request);
-		}
-
-		#endregion
-
-		#region Torrent Start Now
-
-		/// <summary>
-		/// Start now torrents (API: torrent-start-now)
-		/// </summary>
-		/// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
-		public async Task TorrentStartNowAsync(object[] ids)
-		{
-			var request = new TransmissionRequest("torrent-start-now", new Dictionary<string, object> { { "ids", ids } });
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Start now recently active torrents (API: torrent-start-now)
-		/// </summary>
-		public async Task TorrentStartNowAsync()
-		{
-			var request = new TransmissionRequest("torrent-start-now", new Dictionary<string, object> { { "ids", "recently-active" } });
-			var response = await SendRequestAsync(request);
-		}
-
-		#endregion
-
-		#region Torrent Stop
-
-		/// <summary>
-		/// Stop torrents (API: torrent-stop)
-		/// </summary>
-		/// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
-		public async Task TorrentStopAsync(object[] ids)
-		{
-			var request = new TransmissionRequest("torrent-stop", new Dictionary<string, object> { { "ids", ids } });
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Stop recently active torrents (API: torrent-stop)
-		/// </summary>
-		public async Task TorrentStopAsync()
-		{
-			var request = new TransmissionRequest("torrent-stop", new Dictionary<string, object> { { "ids", "recently-active" } });
-			var response = await SendRequestAsync(request);
-		}
-
-		#endregion
-
-		#region Torrent Verify
-
-		/// <summary>
-		/// Verify torrents (API: torrent-verify)
-		/// </summary>
-		/// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
-		public async Task TorrentVerifyAsync(object[] ids)
-		{
-			var request = new TransmissionRequest("torrent-verify", new Dictionary<string, object> { { "ids", ids } });
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Verify recently active torrents (API: torrent-verify)
-		/// </summary>
-		public async Task TorrentVerifyAsync()
-		{
-			var request = new TransmissionRequest("torrent-verify", new Dictionary<string, object> { { "ids", "recently-active" } });
-			var response = await SendRequestAsync(request);
-		}
-		#endregion
-
-		/// <summary>
-		/// Move torrents in queue on top (API: queue-move-top)
-		/// </summary>
-		/// <param name="ids">Torrents id</param>
-		public async Task TorrentQueueMoveTopAsync(int[] ids)
-		{
-			var request = new TransmissionRequest("queue-move-top", new Dictionary<string, object> { { "ids", ids } });
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Move up torrents in queue (API: queue-move-up)
-		/// </summary>
-		/// <param name="ids"></param>
-		public async Task TorrentQueueMoveUpAsync(int[] ids)
-		{
-			var request = new TransmissionRequest("queue-move-up", new Dictionary<string, object> { { "ids", ids } });
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Move down torrents in queue (API: queue-move-down)
-		/// </summary>
-		/// <param name="ids"></param>
-		public async Task TorrentQueueMoveDownAsync(int[] ids)
-		{
-			var request = new TransmissionRequest("queue-move-down", new Dictionary<string, object> { { "ids", ids } });
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Move torrents to bottom in queue  (API: queue-move-bottom)
-		/// </summary>
-		/// <param name="ids"></param>
-		public async Task TorrentQueueMoveBottomAsync(int[] ids)
-		{
-			var request = new TransmissionRequest("queue-move-bottom", new Dictionary<string, object> { { "ids", ids } });
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Set new location for torrents files (API: torrent-set-location)
-		/// </summary>
-		/// <param name="ids">Torrent ids</param>
-		/// <param name="location">The new torrent location</param>
-		/// <param name="move">Move from previous location</param>
-		public async Task TorrentSetLocationAsync(int[] ids, string location, bool move)
-		{
-			var arguments = new Dictionary<string, object>();
-			arguments.Add("ids", ids);
-			arguments.Add("location", location);
-			arguments.Add("move", move);
-
-			var request = new TransmissionRequest("torrent-set-location", arguments);
-			var response = await SendRequestAsync(request);
-		}
-
-		/// <summary>
-		/// Rename a file or directory in a torrent (API: torrent-rename-path)
-		/// </summary>
-		/// <param name="id">The torrent whose path will be renamed</param>
-		/// <param name="path">The path to the file or folder that will be renamed</param>
-		/// <param name="name">The file or folder's new name</param>
-		public async Task<RenameTorrentInfo> TorrentRenamePathAsync(int id, string path, string name)
-		{
-			var arguments = new Dictionary<string, object>();
-			arguments.Add("ids", new int[] { id });
-			arguments.Add("path", path);
-			arguments.Add("name", name);
-
-			var request = new TransmissionRequest("torrent-rename-path", arguments);
-			var response = await SendRequestAsync(request);
-
-			var result = response.Deserialize<RenameTorrentInfo>();
-
-			return result;
-		}
-
-		//method name not recognized
-		///// <summary>
-		///// Reannounce torrent (API: torrent-reannounce)
-		///// </summary>
-		///// <param name="ids"></param>
-		//public void ReannounceTorrents(object[] ids)
-		//{
-		//    var arguments = new Dictionary<string, object>();
-		//    arguments.Add("ids", ids);
-
-		//    var request = new TransmissionRequest("torrent-reannounce", arguments);
-		//    var response = SendRequest(request);
-		//}
-
-		#endregion
-
-		#region System
-
-		/// <summary>
-		/// See if your incoming peer port is accessible from the outside world (API: port-test)
-		/// </summary>
-		/// <returns>Accessible state</returns>
-		public async Task<bool> PortTestAsync()
-		{
-			var request = new TransmissionRequest("port-test");
-			var response = await SendRequestAsync(request);
-
-			var data = response.Deserialize<JObject>();
-			var result = (bool)data.GetValue("port-is-open");
-			return result;
-		}
-
-		/// <summary>
-		/// Update blocklist (API: blocklist-update)
-		/// </summary>
-		/// <returns>Blocklist size</returns>
-		public async Task<int> BlocklistUpdateAsync()
-		{
-			var request = new TransmissionRequest("blocklist-update");
-			var response = await SendRequestAsync(request);
-
-			var data = response.Deserialize<JObject>();
-			var result = (int)data.GetValue("blocklist-size");
-			return result;
-		}
-
-		/// <summary>
-		/// Get free space is available in a client-specified folder.
-		/// </summary>
-		/// <param name="path">The directory to query</param>
-		public async Task<long> FreeSpaceAsync(string path)
-		{
-			var arguments = new Dictionary<string, object>();
-			arguments.Add("path", path);
-
-			var request = new TransmissionRequest("free-space", arguments);
-			var response = await SendRequestAsync(request);
-
-			var data = response.Deserialize<JObject>();
-			var result = (long)data.GetValue("size-bytes");
-			return result;
-		}
+        {
+            var arguments = new Dictionary<string, object>();
+
+            arguments.Add("ids", ids);
+            arguments.Add("delete-local-data", deleteData);
+
+            var request = new TransmissionRequest("torrent-remove", arguments);
+            var response = await SendRequestAsync(request);
+        }
+
+        #region Torrent Start
+
+        /// <summary>
+        /// Start torrents (API: torrent-start)
+        /// </summary>
+        /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
+        public async Task TorrentStartAsync(object[] ids)
+        {
+            var request = new TransmissionRequest("torrent-start", new Dictionary<string, object> { { "ids", ids } });
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Start recently active torrents (API: torrent-start)
+        /// </summary>
+        public async Task TorrentStartAsync()
+        {
+            var request = new TransmissionRequest("torrent-start", new Dictionary<string, object> { { "ids", "recently-active" } });
+            var response = await SendRequestAsync(request);
+        }
+
+        #endregion
+
+        #region Torrent Start Now
+
+        /// <summary>
+        /// Start now torrents (API: torrent-start-now)
+        /// </summary>
+        /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
+        public async Task TorrentStartNowAsync(object[] ids)
+        {
+            var request = new TransmissionRequest("torrent-start-now", new Dictionary<string, object> { { "ids", ids } });
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Start now recently active torrents (API: torrent-start-now)
+        /// </summary>
+        public async Task TorrentStartNowAsync()
+        {
+            var request = new TransmissionRequest("torrent-start-now", new Dictionary<string, object> { { "ids", "recently-active" } });
+            var response = await SendRequestAsync(request);
+        }
+
+        #endregion
+
+        #region Torrent Stop
+
+        /// <summary>
+        /// Stop torrents (API: torrent-stop)
+        /// </summary>
+        /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
+        public async Task TorrentStopAsync(object[] ids)
+        {
+            var request = new TransmissionRequest("torrent-stop", new Dictionary<string, object> { { "ids", ids } });
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Stop recently active torrents (API: torrent-stop)
+        /// </summary>
+        public async Task TorrentStopAsync()
+        {
+            var request = new TransmissionRequest("torrent-stop", new Dictionary<string, object> { { "ids", "recently-active" } });
+            var response = await SendRequestAsync(request);
+        }
+
+        #endregion
+
+        #region Torrent Verify
+
+        /// <summary>
+        /// Verify torrents (API: torrent-verify)
+        /// </summary>
+        /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
+        public async Task TorrentVerifyAsync(object[] ids)
+        {
+            var request = new TransmissionRequest("torrent-verify", new Dictionary<string, object> { { "ids", ids } });
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Verify recently active torrents (API: torrent-verify)
+        /// </summary>
+        public async Task TorrentVerifyAsync()
+        {
+            var request = new TransmissionRequest("torrent-verify", new Dictionary<string, object> { { "ids", "recently-active" } });
+            var response = await SendRequestAsync(request);
+        }
+
+        #endregion
+
+        #region Torrent Reannounce
+
+        /// <summary>
+        /// Reannounce torrents (API: torrent-reannounce)
+        /// </summary>
+        /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
+        public async Task TorrentReannounceAsync(object[] ids)
+        {
+            var request = new TransmissionRequest("torrent-reannounce", new Dictionary<string, object> { { "ids", ids } });
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Reannounce recently active torrents (API: torrent-reannounce)
+        /// </summary>
+        public async Task TorrentReannounceAsync()
+        {
+            var request = new TransmissionRequest("torrent-reannounce", new Dictionary<string, object> { { "ids", "recently-active" } });
+            var response = await SendRequestAsync(request);
+        }
+
+        #endregion
+
+        #region Torrent Queue
+
+        /// <summary>
+        /// Move torrents in queue on top (API: queue-move-top)
+        /// </summary>
+        /// <param name="ids">Torrents id</param>
+        public async Task TorrentQueueMoveTopAsync(int[] ids)
+        {
+            var request = new TransmissionRequest("queue-move-top", new Dictionary<string, object> { { "ids", ids } });
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Move up torrents in queue (API: queue-move-up)
+        /// </summary>
+        /// <param name="ids"></param>
+        public async Task TorrentQueueMoveUpAsync(int[] ids)
+        {
+            var request = new TransmissionRequest("queue-move-up", new Dictionary<string, object> { { "ids", ids } });
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Move down torrents in queue (API: queue-move-down)
+        /// </summary>
+        /// <param name="ids"></param>
+        public async Task TorrentQueueMoveDownAsync(int[] ids)
+        {
+            var request = new TransmissionRequest("queue-move-down", new Dictionary<string, object> { { "ids", ids } });
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Move torrents to bottom in queue  (API: queue-move-bottom)
+        /// </summary>
+        /// <param name="ids"></param>
+        public async Task TorrentQueueMoveBottomAsync(int[] ids)
+        {
+            var request = new TransmissionRequest("queue-move-bottom", new Dictionary<string, object> { { "ids", ids } });
+            var response = await SendRequestAsync(request);
+        }
+
+        #endregion
+
+        /// <summary>
+        /// Set new location for torrents files (API: torrent-set-location)
+        /// </summary>
+        /// <param name="ids">Torrent ids</param>
+        /// <param name="location">The new torrent location</param>
+        /// <param name="move">Move from previous location</param>
+        public async Task TorrentSetLocationAsync(int[] ids, string location, bool move)
+        {
+            var arguments = new Dictionary<string, object>();
+            arguments.Add("ids", ids);
+            arguments.Add("location", location);
+            arguments.Add("move", move);
+
+            var request = new TransmissionRequest("torrent-set-location", arguments);
+            var response = await SendRequestAsync(request);
+        }
+
+        /// <summary>
+        /// Rename a file or directory in a torrent (API: torrent-rename-path)
+        /// </summary>
+        /// <param name="id">The torrent whose path will be renamed</param>
+        /// <param name="path">The path to the file or folder that will be renamed</param>
+        /// <param name="name">The file or folder's new name</param>
+        public async Task<RenameTorrentInfo> TorrentRenamePathAsync(int id, string path, string name)
+        {
+            var arguments = new Dictionary<string, object>();
+            arguments.Add("ids", new int[] { id });
+            arguments.Add("path", path);
+            arguments.Add("name", name);
+
+            var request = new TransmissionRequest("torrent-rename-path", arguments);
+            var response = await SendRequestAsync(request);
+
+            var result = response.Deserialize<RenameTorrentInfo>();
+
+            return result;
+        }
+
+        #endregion
+
+        #region Bandwidth Groups
+
+        /// <summary>
+        /// Get bandwidth groups (API: group-get)
+        /// </summary>
+        /// <returns></returns>
+        public async Task<BandwidthGroup[]> BandwidthGroupGetAsync()
+        {
+            var request = new TransmissionRequest("group-get");
+
+            var response = await SendRequestAsync(request);
+            var result = response.Deserialize<BandwidthGroup[]>();
+
+            return result;
+        }
+
+        /// <summary>
+        /// Get bandwidth groups (API: group-get)
+        /// </summary>
+        /// <param name="groups">Optional names of groups to get</param>
+        /// <returns></returns>
+        public async Task<BandwidthGroup[]> BandwidthGroupGetAsync(string[] groups)
+        {
+            var arguments = new Dictionary<string, object>();
+            arguments.Add("group", groups);
+
+            var request = new TransmissionRequest("group-get", arguments);
+
+            var response = await SendRequestAsync(request);
+            var result = response.Deserialize<BandwidthGroup[]>();
+
+            return result;
+        }
+
+        /// <summary>
+        /// Set bandwidth groups (API: group-set)
+        /// </summary>
+        /// <param name="group">A bandwidth group to set</param>
+        public async Task BandwidthGroupSetAsync(BandwidthGroupSettings group)
+        {
+            var request = new TransmissionRequest("group-set", group);
+            var response = await SendRequestAsync(request);
+        }
+
+        #endregion
+
+        #region System
+
+        /// <summary>
+        /// See if your incoming peer port is accessible from the outside world (API: port-test)
+        /// </summary>
+        /// <returns>A Tuple with a boolean of whether the port test succeeded, and a PortTestProtocol enum of which protocol was used for the test</returns>
+        public async Task<Tuple<bool, PortTestProtocol>> PortTestAsync()
+        {
+            var request = new TransmissionRequest("port-test");
+            var response = await SendRequestAsync(request);
+
+            var data = response.Deserialize<JObject>();
+            var result = (bool)data.GetValue("port-is-open");
+            PortTestProtocol protocol = PortTestProtocol.Unknown;
+            if (data.TryGetValue("ipProtocol", out var protocolValue))
+            {
+                switch ((string)protocolValue)
+                {
+                    case "ipv4": protocol = PortTestProtocol.IPv4; break;
+                    case "ipv6": protocol = PortTestProtocol.IPV6; break;
+                }
+            }
+            return new Tuple<bool, PortTestProtocol>(result, protocol);
+        }
+
+        /// <summary>
+        /// Update blocklist (API: blocklist-update)
+        /// </summary>
+        /// <returns>Blocklist size</returns>
+        public async Task<int> BlocklistUpdateAsync()
+        {
+            var request = new TransmissionRequest("blocklist-update");
+            var response = await SendRequestAsync(request);
+
+            var data = response.Deserialize<JObject>();
+            var result = (int)data.GetValue("blocklist-size");
+            return result;
+        }
+
+        /// <summary>
+        /// Get free space is available in a client-specified folder.
+        /// </summary>
+        /// <param name="path">The directory to query</param>
+        public async Task<FreeSpace> FreeSpaceAsync(string path)
+        {
+            var arguments = new Dictionary<string, object>();
+            arguments.Add("path", path);
+
+            var request = new TransmissionRequest("free-space", arguments);
+            var response = await SendRequestAsync(request);
+
+            var data = response.Deserialize<FreeSpace>();
+            return data;
+        }
 
         #endregion
 
diff --git a/Transmission.API.RPC/Client.cs b/Transmission.API.RPC/Client.cs
index fa2f484..46d2b2f 100644
--- a/Transmission.API.RPC/Client.cs
+++ b/Transmission.API.RPC/Client.cs
@@ -2,6 +2,8 @@
 using System.Text;
 using Transmission.API.RPC.Entity;
 using Transmission.API.RPC.Arguments;
+using System.IO;
+using System.Xml.Linq;
 
 namespace Transmission.API.RPC
 {
@@ -108,6 +110,18 @@ public SessionInfo GetSessionInformation()
             return task.Result;
         }
 
+        /// <summary>
+        /// Get information of current session (API: session-get)
+        /// </summary>
+		/// <param name="fields">Optional fields of session information</param>
+        /// <returns>Session information</returns>
+        public SessionInfo GetSessionInformation(string[] fields)
+        {
+            var task = GetSessionInformationAsync(fields);
+            task.WaitAndUnwrapException();
+            return task.Result;
+        }
+
         #endregion
 
         #region Torrents methods
@@ -214,6 +228,7 @@ public void TorrentStop()
         #endregion
 
         #region Torrent Verify
+
         /// <summary>
         /// Verify torrents (API: torrent-verify)
         /// </summary>
@@ -230,8 +245,32 @@ public void TorrentVerify()
         {
             TorrentVerifyAsync().WaitAndUnwrapException();
         }
+
+        #endregion
+
+        #region Torrent Reannounce
+
+        /// <summary>
+        /// Reannounce torrents (API: torrent-reannounce)
+        /// </summary>
+        /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
+        public void TorrentReannounce(object[] ids)
+        {
+            TorrentReannounceAsync(ids).WaitAndUnwrapException();
+        }
+
+        /// <summary>
+        /// Reannounce recently active torrents (API: torrent-reannounce)
+        /// </summary>
+        public void TorrentReannounce()
+        {
+            TorrentReannounceAsync().WaitAndUnwrapException();
+        }
+
         #endregion
 
+        #region Torrent Queue
+
         /// <summary>
         /// Move torrents in queue on top (API: queue-move-top)
         /// </summary>
@@ -268,6 +307,8 @@ public void TorrentQueueMoveBottom(int[] ids)
             TorrentQueueMoveBottomAsync(ids).WaitAndUnwrapException();
         }
 
+        #endregion
+
         /// <summary>
         /// Set new location for torrents files (API: torrent-set-location)
         /// </summary>
@@ -292,15 +333,41 @@ public RenameTorrentInfo TorrentRenamePath(int id, string path, string name)
             return task.Result;
         }
 
-        //method name not recognized
-        ///// <summary>
-        ///// Reannounce torrent (API: torrent-reannounce)
-        ///// </summary>
-        ///// <param name="ids"></param>
-        //public void ReannounceTorrents(object[] ids)
-        //{
-        //    ReannounceTorrentsAsync(ids).WaitAndUnwrapException();
-        //}
+        #endregion
+
+        #region Bandwidth Groups
+
+        /// <summary>
+        /// Get bandwidth groups (API: group-get)
+        /// </summary>
+        /// <returns></returns>
+        public BandwidthGroup[] BandwidthGroupGet()
+        {
+            var task = BandwidthGroupGetAsync();
+            task.WaitAndUnwrapException();
+            return task.Result;
+        }
+
+        /// <summary>
+        /// Get bandwidth groups (API: group-get)
+        /// </summary>
+        /// <param name="groups">Optional names of groups to get</param>
+        /// <returns></returns>
+        public BandwidthGroup[] BandwidthGroupGet(string[] groups)
+        {
+            var task = BandwidthGroupGetAsync(groups);
+            task.WaitAndUnwrapException();
+            return task.Result;
+        }
+
+        /// <summary>
+        /// Set bandwidth groups (API: group-set)
+        /// </summary>
+        /// <param name="group">A bandwidth group to set</param>
+        public void BandwidthGroupSet(BandwidthGroupSettings group)
+        {
+            BandwidthGroupGetAsync().WaitAndUnwrapException();
+        }
 
         #endregion
 
@@ -309,7 +376,7 @@ public RenameTorrentInfo TorrentRenamePath(int id, string path, string name)
         /// See if your incoming peer port is accessible from the outside world (API: port-test)
         /// </summary>
         /// <returns>Accessible state</returns>
-        public bool PortTest()
+        public Tuple<bool, PortTestProtocol> PortTest()
         {
             var task = PortTestAsync();
             task.WaitAndUnwrapException();
@@ -331,12 +398,13 @@ public int BlocklistUpdate()
         /// Get free space is available in a client-specified folder.
         /// </summary>
         /// <param name="path">The directory to query</param>
-        public long FreeSpace(string path)
+        public FreeSpace FreeSpace(string path)
         {
             var task = FreeSpaceAsync(path);
             task.WaitAndUnwrapException();
             return task.Result;
         }
+
         #endregion
     }
 }
diff --git a/Transmission.API.RPC/Entity/BandwidthGroup.cs b/Transmission.API.RPC/Entity/BandwidthGroup.cs
new file mode 100644
index 0000000..9d6df63
--- /dev/null
+++ b/Transmission.API.RPC/Entity/BandwidthGroup.cs
@@ -0,0 +1,46 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Transmission.API.RPC.Entity
+{
+    public class BandwidthGroup
+    {
+        /// <summary>
+        /// Session limits are honored
+        /// </summary>
+        [JsonProperty("honorsSessionLimits")]
+        public bool? HonorsSessionLimits { get; set; }
+
+        /// <summary>
+        /// Name of the bandwidth group
+        /// </summary>
+        [JsonProperty("name")]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Max global download speed of this bandwidth group (KBps)
+        /// </summary>
+        [JsonProperty("speed-limit-down")]
+        public long? SpeedLimitDown { get; set; }
+
+        /// <summary>
+        /// True means enabled
+        /// </summary>
+        [JsonProperty("speed-limit-down-enabled")]
+        public bool? SpeedLimitDownEnabled { get; set; }
+
+        /// <summary>
+        /// Max global upload speed of this bandwidth group (KBps)
+        /// </summary>
+        [JsonProperty("speed-limit-up")]
+        public long? SpeedLimitUp { get; set; }
+
+        /// <summary>
+        /// True means enabled
+        /// </summary>
+        [JsonProperty("speed-limit-up-enabled")]
+        public bool? SpeedLimitUpEnabled { get; set; }
+    }
+}
diff --git a/Transmission.API.RPC/Entity/FreeSpace.cs b/Transmission.API.RPC/Entity/FreeSpace.cs
new file mode 100644
index 0000000..71b80b2
--- /dev/null
+++ b/Transmission.API.RPC/Entity/FreeSpace.cs
@@ -0,0 +1,33 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Transmission.API.RPC.Entity
+{
+    /// <summary>
+    /// FreeSpace
+    /// </summary>
+    public class FreeSpace
+    {
+        /// <summary>
+        /// Path of the queried directory
+        /// </summary>
+        [JsonProperty("path")]
+        public string Path { get; set; }
+
+        /// <summary>
+        /// The size, in bytes, of the free space in that directory
+        /// </summary>
+        [JsonProperty("size-bytes")]
+        public long? SizeBytes { get; set; }
+
+        /// <summary>
+        /// the total capacity, in bytes, of that directory
+        /// </summary>
+        [JsonProperty("total_size")]
+        public long? TotalSize { get; set; }
+    }
+}
diff --git a/Transmission.API.RPC/Entity/IntOrArrayConverter.cs b/Transmission.API.RPC/Entity/IntOrArrayConverter.cs
new file mode 100644
index 0000000..64b3b8b
--- /dev/null
+++ b/Transmission.API.RPC/Entity/IntOrArrayConverter.cs
@@ -0,0 +1,80 @@
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JDRemote.Backends.Transmission
+{
+    /// <summary>
+    /// JSON type converter for type long[] or long or int[] or int
+    /// This is needed because the Availability field was introduced in Transmission 4.0.0
+    /// On earlier versions, the field defaults to "0" and decoding to an array fails.
+    /// </summary>
+    public class IntOrArrayConverter : JsonConverter
+    {
+        /// <summary>
+        /// Returns true whether the object is of type long[] or long or int[] or int
+        /// </summary>
+        /// <param name="objectType"></param>
+        /// <returns></returns>
+        public override bool CanConvert(Type objectType)
+        {
+            return objectType == typeof(long[]) || objectType == typeof(long) || objectType == typeof(int[]) || objectType == typeof(int) || objectType == typeof(long?[]) || objectType == typeof(long?) || objectType == typeof(int?[]) || objectType == typeof(int?);
+        }
+
+        /// <summary>
+        /// Read long[] or single long (not array) from json
+        /// </summary>
+        /// <param name="reader"></param>
+        /// <param name="objectType"></param>
+        /// <param name="existingValue"></param>
+        /// <param name="serializer"></param>
+        /// <returns></returns>
+        /// <exception cref="JsonSerializationException"></exception>
+        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+        {
+            JToken token = JToken.Load(reader);
+
+            if (token.Type == JTokenType.Integer)
+            {
+                // Return an array with one element if it's a single integer
+                if (objectType == typeof(long[])) return new long[] { (long)token };
+                if (objectType == typeof(long?[])) return new long?[] { (long?)token };
+            }
+            else if (token.Type == JTokenType.Array)
+            {
+                // Return the integer array if it's a JSON array
+                if (objectType == typeof(long[])) return token.ToObject<long[]>();
+                if (objectType == typeof(long?[])) return token.ToObject<long?[]>();
+            }
+
+            throw new JsonSerializationException("Unexpected token type");
+        }
+
+        /// <summary>
+        /// Write long[] to json
+        /// </summary>
+        /// <param name="writer"></param>
+        /// <param name="value"></param>
+        /// <param name="serializer"></param>
+        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+        {
+            if (value != null)
+            {
+                long?[] longArray = (long?[])value;
+                if (longArray.Length == 1)
+                {
+                    writer.WriteValue(longArray[0]);
+                }
+                else
+                {
+                    JArray array = new JArray(longArray);
+                    array.WriteTo(writer);
+                }
+            }
+        }
+    }
+}
diff --git a/Transmission.API.RPC/Entity/NewTorrentInfo.cs b/Transmission.API.RPC/Entity/NewTorrentInfo.cs
index ee5ac79..d3e82be 100644
--- a/Transmission.API.RPC/Entity/NewTorrentInfo.cs
+++ b/Transmission.API.RPC/Entity/NewTorrentInfo.cs
@@ -30,5 +30,10 @@ public class NewTorrentInfo
 		[JsonProperty("hashString")]
 		public string HashString { get; set; }
 
+        /// <summary>
+        /// Whether the torrent is a duplicate of an existing torrent (add failed)
+        /// </summary>
+        public bool Duplicate { get; set; }
+
 	}
 }
diff --git a/Transmission.API.RPC/Entity/SessionInfo.cs b/Transmission.API.RPC/Entity/SessionInfo.cs
index f5137d4..2b4fba3 100644
--- a/Transmission.API.RPC/Entity/SessionInfo.cs
+++ b/Transmission.API.RPC/Entity/SessionInfo.cs
@@ -18,7 +18,7 @@ public class SessionInfo
         /// Max global download speed (KBps)
         /// </summary>
         [JsonProperty("alt-speed-down")]
-        public int? AlternativeSpeedDown { get; set; }
+        public long? AlternativeSpeedDown { get; set; }
 
         /// <summary>
         /// True means use the alt speeds
@@ -30,7 +30,7 @@ public class SessionInfo
         /// When to turn on alt speeds (units: minutes after midnight)
         /// </summary>
         [JsonProperty("alt-speed-time-begin")]
-        public int? AlternativeSpeedTimeBegin { get; set; }
+        public long? AlternativeSpeedTimeBegin { get; set; }
 
         /// <summary>
         /// True means the scheduled on/off times are used
@@ -42,25 +42,25 @@ public class SessionInfo
         /// When to turn off alt speeds
         /// </summary>
         [JsonProperty("alt-speed-time-end")]
-        public int? AlternativeSpeedTimeEnd { get; set; }
+        public long? AlternativeSpeedTimeEnd { get; set; }
 
         /// <summary>
         /// What day(s) to turn on alt speeds
         /// </summary>
         [JsonProperty("alt-speed-time-day")]
-        public int? AlternativeSpeedTimeDay { get; set; }
+        public long? AlternativeSpeedTimeDay { get; set; }
 
         /// <summary>
         /// Max global upload speed (KBps)
         /// </summary>
         [JsonProperty("alt-speed-up")]
-        public int? AlternativeSpeedUp { get; set; }
+        public long? AlternativeSpeedUp { get; set; }
 
         /// <summary>
         /// Location of the blocklist to use for "blocklist-update"
         /// </summary>
         [JsonProperty("blocklist-url")]
-        public string BlocklistURL { get; set; }
+        public string BlocklistUrl { get; set; }
 
         /// <summary>
         /// True means enabled
@@ -68,11 +68,29 @@ public class SessionInfo
         [JsonProperty("blocklist-enabled")]
         public bool? BlocklistEnabled { get; set; }
 
+        /// <summary>
+        /// Number of rules in the blocklist
+        /// </summary>
+        [JsonProperty("blocklist-size")]
+        public long? BlocklistSize { get; set; }
+
         /// <summary>
         /// Maximum size of the disk cache (MB)
         /// </summary>
         [JsonProperty("cache-size-mb")]
-        public int? CacheSizeMB { get; set; }
+        public long? CacheSizeMb { get; set; }
+
+        /// <summary>
+        /// Default announce URLs, one per line, and a blank line between tiers
+        /// </summary>
+        [JsonProperty("default-trackers")]
+        public string DefaultTrackers { get; set; }
+
+        /// <summary>
+        /// Allow DHT in public torrents
+        /// </summary>
+        [JsonProperty("dht-enabled")]
+        public bool? DhtEnabled { get; set; }
 
         /// <summary>
         /// Default path to download torrents
@@ -91,7 +109,7 @@ public class SessionInfo
         /// Max number of torrents to download at once (see download-queue-enabled)
         /// </summary>
         [JsonProperty("download-queue-size")]
-        public int? DownloadQueueSize { get; set; }
+        public long? DownloadQueueSize { get; set; }
 
         /// <summary>
         /// If true, limit how many torrents can be downloaded at once
@@ -99,12 +117,6 @@ public class SessionInfo
         [JsonProperty("download-queue-enabled")]
         public bool? DownloadQueueEnabled { get; set; }
 
-        /// <summary>
-        /// True means allow dht in public torrents
-        /// </summary>
-        [JsonProperty("dht-enabled")]
-        public bool? DHTEnabled { get; set; }
-
         /// <summary>
         /// "required", "preferred", "tolerated"
         /// </summary>
@@ -115,7 +127,7 @@ public class SessionInfo
         /// Torrents we're seeding will be stopped if they're idle for this long
         /// </summary>
         [JsonProperty("idle-seeding-limit")]
-        public int? IdleSeedingLimit { get; set; }
+        public long? IdleSeedingLimit { get; set; }
 
         /// <summary>
         /// True if the seeding inactivity limit is honored by default
@@ -139,19 +151,19 @@ public class SessionInfo
         /// True means allow Local Peer Discovery in public torrents
         /// </summary>
         [JsonProperty("lpd-enabled")]
-        public bool? LPDEnabled { get; set; }
+        public bool? LpdEnabled { get; set; }
 
         /// <summary>
         /// Maximum global number of peers
         /// </summary>
         [JsonProperty("peer-limit-global")]
-        public int? PeerLimitGlobal { get; set; }
+        public long? PeerLimitGlobal { get; set; }
 
         /// <summary>
         /// Maximum global number of peers
         /// </summary>
         [JsonProperty("peer-limit-per-torrent")]
-        public int? PeerLimitPerTorrent { get; set; }
+        public long? PeerLimitPerTorrent { get; set; }
 
         /// <summary>
         /// True means allow pex in public torrents
@@ -163,7 +175,7 @@ public class SessionInfo
         /// Port number
         /// </summary>
         [JsonProperty("peer-port")]
-        public int? PeerPort { get; set; }
+        public long? PeerPort { get; set; }
 
         /// <summary>
         /// True means pick a random peer port on launch
@@ -187,19 +199,31 @@ public class SessionInfo
         /// Torrents that are idle for N minuets aren't counted toward seed-queue-size or download-queue-size
         /// </summary>
         [JsonProperty("queue-stalled-minutes")]
-        public int? QueueStalledMinutes { get; set; }
+        public long? QueueStalledMinutes { get; set; }
 
         /// <summary>
         /// True means append ".part" to incomplete files
         /// </summary>
         [JsonProperty("rename-partial-files")]
         public bool? RenamePartialFiles { get; set; }
-
+        
         /// <summary>
         /// Session ID
         /// </summary>
         [JsonProperty("session-id")]
-        public string SessionID { get; set; }
+        public string SessionId { get; set; }
+
+        /// <summary>
+        /// Filename of the script to run
+        /// </summary>
+        [JsonProperty("script-torrent-added-filename")]
+        public string ScriptTorrentAddedFilename { get; set; }
+
+        /// <summary>
+        /// Whether or not to call the "added" script
+        /// </summary>
+        [JsonProperty("script-torrent-added-enabled")]
+        public bool? ScriptTorrentAddedEnabled { get; set; }
 
         /// <summary>
         /// Filename of the script to run
@@ -213,6 +237,18 @@ public class SessionInfo
         [JsonProperty("script-torrent-done-enabled")]
         public bool? ScriptTorrentDoneEnabled { get; set; }
 
+        /// <summary>
+        /// Filename of the script to run
+        /// </summary>
+        [JsonProperty("script-torrent-done-seeding-filename")]
+        public string ScriptTorrentDoneSeedingFilename { get; set; }
+
+        /// <summary>
+        /// Whether or not to call the "done seeding" script
+        /// </summary>
+        [JsonProperty("script-torrent-done-seeding-enabled")]
+        public bool? ScriptTorrentDoneSeedingEnabled { get; set; }
+
         /// <summary>
         /// The default seed ratio for torrents to use
         /// </summary>
@@ -229,7 +265,7 @@ public class SessionInfo
         /// Max number of torrents to uploaded at once (see seed-queue-enabled)
         /// </summary>
         [JsonProperty("seed-queue-size")]
-        public int? SeedQueueSize { get; set; }
+        public long? SeedQueueSize { get; set; }
 
         /// <summary>
         /// If true, limit how many torrents can be uploaded at once
@@ -241,7 +277,7 @@ public class SessionInfo
         /// Max global download speed (KBps)
         /// </summary>
         [JsonProperty("speed-limit-down")]
-        public int? SpeedLimitDown { get; set; }
+        public long? SpeedLimitDown { get; set; }
 
         /// <summary>
         /// True means enabled
@@ -253,7 +289,7 @@ public class SessionInfo
         ///  max global upload speed (KBps)
         /// </summary>
         [JsonProperty("speed-limit-up")]
-        public int? SpeedLimitUp { get; set; }
+        public long? SpeedLimitUp { get; set; }
 
         /// <summary>
         /// True means enabled
@@ -285,12 +321,6 @@ public class SessionInfo
         [JsonProperty("utp-enabled")]
         public bool? UtpEnabled { get; set; }
 
-        /// <summary>
-        /// Number of rules in the blocklist
-        /// </summary>
-        [JsonProperty("blocklist-size")]
-        public int BlocklistSize{ get; set; }
-
         /// <summary>
         /// Location of transmission's configuration directory
         /// </summary>
@@ -301,16 +331,22 @@ public class SessionInfo
         /// The current RPC API version
         /// </summary>
         [JsonProperty("rpc-version")]
-        public int RpcVersion{ get; set; }
+        public long? RpcVersion{ get; set; }
 
         /// <summary>
         /// The minimum RPC API version supported
         /// </summary>
         [JsonProperty("rpc-version-minimum")]
-        public int RpcVersionMinimum{ get; set; }
+        public long? RpcVersionMinimum { get; set; }
+
+        /// <summary>
+        /// Current RPC API version in a semver-compatible string
+        /// </summary>
+        [JsonProperty("rpc-version-semver")]
+        public string RpcVersionSemver { get; set; }
 
         /// <summary>
-        /// Long version string "$version ($revision)"
+        /// long? version string "$version ($revision)"
         /// </summary>
         [JsonProperty("version")]
         public string Version{ get; set; }
diff --git a/Transmission.API.RPC/Entity/Statistic.cs b/Transmission.API.RPC/Entity/Statistic.cs
index 22668bf..d54e36b 100644
--- a/Transmission.API.RPC/Entity/Statistic.cs
+++ b/Transmission.API.RPC/Entity/Statistic.cs
@@ -16,31 +16,31 @@ public class Statistic
         /// Active torrent count
         /// </summary>
         [JsonProperty("activeTorrentCount")]
-        public int ActiveTorrentCount { get; set; }
+        public long? ActiveTorrentCount { get; set; }
 
         /// <summary>
         /// Download speed
         /// </summary>
         [JsonProperty("downloadSpeed")]
-        public int downloadSpeed{ get; set; }
+        public long? downloadSpeed{ get; set; }
 
         /// <summary>
         /// Paused torrent count
         /// </summary>
         [JsonProperty("pausedTorrentCount")]
-        public int pausedTorrentCount{ get; set; }
+        public long? pausedTorrentCount{ get; set; }
 
         /// <summary>
         /// Torrent count
         /// </summary>
         [JsonProperty("torrentCount")]
-        public int torrentCount{ get; set; }
+        public long? torrentCount{ get; set; }
 
         /// <summary>
         /// Upload speed
         /// </summary>
         [JsonProperty("uploadSpeed")]
-        public int uploadSpeed{ get; set; }
+        public long? uploadSpeed{ get; set; }
    
         /// <summary>
         /// Cumulative stats
@@ -64,30 +64,30 @@ public class CommonStatistic
         /// Uploaded bytes
         /// </summary>
         [JsonProperty("uploadedBytes")]
-        public double uploadedBytes{ get; set; }
+        public long? UploadedBytes{ get; set; }
         
         /// <summary>
         /// Downloaded bytes
         /// </summary>
         [JsonProperty("downloadedBytes")]
-        public double DownloadedBytes{ get; set; }
+        public long? DownloadedBytes{ get; set; }
 
         /// <summary>
         /// Files added
         /// </summary>
         [JsonProperty("filesAdded")]
-        public int FilesAdded{ get; set; }
+        public long? FilesAdded{ get; set; }
 
         /// <summary>
         /// Session count
         /// </summary>
         [JsonProperty("SessionCount")]
-        public int SessionCount{ get; set; }
+        public long? SessionCount{ get; set; }
 
         /// <summary>
         /// Seconds active
         /// </summary>
         [JsonProperty("SecondsActive")]
-        public int SecondsActive{ get; set; }
+        public long? SecondsActive{ get; set; }
     }
 }
diff --git a/Transmission.API.RPC/Entity/TorrentInfo.cs b/Transmission.API.RPC/Entity/TorrentInfo.cs
index 3a8cfef..d7ac0e2 100644
--- a/Transmission.API.RPC/Entity/TorrentInfo.cs
+++ b/Transmission.API.RPC/Entity/TorrentInfo.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json;
+using JDRemote.Backends.Transmission;
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -16,25 +17,32 @@ public class TorrentInfo
         /// The torrent's unique Id.
         /// </summary>
         [JsonProperty("id")]
-        public int ID { get; set; }
+        public long? Id { get; set; }
 
         /// <summary>
         /// Activity date
         /// </summary>
         [JsonProperty("activityDate")]
-        public long ActivityDate { get; set; }
+        public long? ActivityDate { get; set; }
 
         /// <summary>
         /// Added date
         /// </summary>
         [JsonProperty("addedDate")]
-        public long AddedDate { get; set; }
+        public long? AddedDate { get; set; }
+
+        /// <summary>
+        /// Availability
+        /// </summary>
+        [JsonProperty("availability")]
+        [JsonConverter(typeof(IntOrArrayConverter))] // Without this converter, Transmission < 4.0.0 leads to an error.
+        public long?[] Availability { get; set; }
 
         /// <summary>
         /// Torrents bandwidth priority
         /// </summary>
         [JsonProperty("bandwidthPriority")]
-        public int BandwidthPriority { get; set; }
+        public long? BandwidthPriority { get; set; }
 
         /// <summary>
         /// Comment
@@ -46,7 +54,7 @@ public class TorrentInfo
         /// Corrupt ever
         /// </summary>
         [JsonProperty("corruptEver")]
-        public int CorruptEver { get; set; }
+        public long? CorruptEver { get; set; }
 
         /// <summary>
         /// Creator
@@ -58,19 +66,19 @@ public class TorrentInfo
         /// Date created
         /// </summary>
         [JsonProperty("dateCreated")]
-        public long DateCreated { get; set; }
+        public long? DateCreated { get; set; }
 
         /// <summary>
         /// Desired available
         /// </summary>
         [JsonProperty("desiredAvailable")]
-        public long DesiredAvailable { get; set; }
+        public long? DesiredAvailable { get; set; }
 
         /// <summary>
         /// Done date
         /// </summary>
         [JsonProperty("doneDate")]
-        public long DoneDate { get; set; }
+        public long? DoneDate { get; set; }
 
         /// <summary>
         /// Download directory
@@ -82,31 +90,31 @@ public class TorrentInfo
         /// Downloaded ever
         /// </summary>
         [JsonProperty("downloadedEver")]
-        public string DownloadedEver { get; set; }
+        public long? DownloadedEver { get; set; }
 
         /// <summary>
         /// Download limit
         /// </summary>
         [JsonProperty("downloadLimit")]
-        public string DownloadLimit { get; set; }
+        public long? DownloadLimit { get; set; }
 
         /// <summary>
         /// Download limited
         /// </summary>
         [JsonProperty("downloadLimited")]
-        public string DownloadLimited { get; set; }
+        public bool? DownloadLimited { get; set; }
 
         /// <summary>
         /// Edit date
         /// </summary>
         [JsonProperty("editDate")]
-        public long EditDate { get; set; }
+        public long? EditDate { get; set; }
 
         /// <summary>
         /// Error
         /// </summary>
         [JsonProperty("error")]
-        public int Error { get; set; }
+        public long? Error { get; set; }
 
         /// <summary>
         /// Error string
@@ -118,19 +126,19 @@ public class TorrentInfo
         /// ETA
         /// </summary>
         [JsonProperty("eta")]
-        public int ETA { get; set; }
+        public long? Eta { get; set; }
 
         /// <summary>
         /// ETA idle
         /// </summary>
         [JsonProperty("etaIdle")]
-        public int ETAIdle { get; set; }
+        public long? EtaIdle { get; set; }
 
         /// <summary>
         /// File count
         /// </summary>
         [JsonProperty("file-count")]
-        public int FileCount { get; set; }
+        public long? FileCount { get; set; }
 
         /// <summary>
         /// Files
@@ -144,6 +152,12 @@ public class TorrentInfo
         [JsonProperty("fileStats")]
         public TransmissionTorrentFileStats[] FileStats { get; set; }
 
+        /// <summary>
+        /// Group
+        /// </summary>
+        [JsonProperty("group")]
+        public string Group { get; set; }
+
         /// <summary>
         /// Hash string
         /// </summary>
@@ -154,37 +168,37 @@ public class TorrentInfo
         /// Have unchecked
         /// </summary>
         [JsonProperty("haveUnchecked")]
-        public int HaveUnchecked { get; set; }
+        public long? HaveUnchecked { get; set; }
 
         /// <summary>
         /// Have valid
         /// </summary>
         [JsonProperty("haveValid")]
-        public long HaveValid { get; set; }
+        public long? HaveValid { get; set; }
 
         /// <summary>
         /// Honors session limits
         /// </summary>
         [JsonProperty("honorsSessionLimits")]
-        public bool HonorsSessionLimits { get; set; }
+        public bool? HonorsSessionLimits { get; set; }
 
         /// <summary>
         /// Is finished
         /// </summary>
         [JsonProperty("isFinished")]
-        public bool IsFinished { get; set; }
+        public bool? IsFinished { get; set; }
 
         /// <summary>
         /// Is private
         /// </summary>
         [JsonProperty("isPrivate")]
-        public bool IsPrivate { get; set; }
+        public bool? IsPrivate { get; set; }
 
         /// <summary>
         /// Is stalled
         /// </summary>
         [JsonProperty("isStalled")]
-        public bool IsStalled { get; set; }
+        public bool? IsStalled { get; set; }
 
         /// <summary>
         /// Labels
@@ -196,7 +210,7 @@ public class TorrentInfo
         /// Left until done
         /// </summary>
         [JsonProperty("leftUntilDone")]
-        public long LeftUntilDone { get; set; }
+        public long? LeftUntilDone { get; set; }
 
         /// <summary>
         /// Magnet link
@@ -208,19 +222,19 @@ public class TorrentInfo
         /// Manual announce time
         /// </summary>
         [JsonProperty("manualAnnounceTime")]
-        public int ManualAnnounceTime { get; set; }
+        public long? ManualAnnounceTime { get; set; }
 
         /// <summary>
         /// Max connected peers
         /// </summary>
         [JsonProperty("maxConnectedPeers")]
-        public int MaxConnectedPeers { get; set; }
+        public long? MaxConnectedPeers { get; set; }
 
         /// <summary>
         /// Metadata percent complete
         /// </summary>
         [JsonProperty("metadataPercentComplete")]
-        public double MetadataPercentComplete { get; set; }
+        public double? MetadataPercentComplete { get; set; }
 
         /// <summary>
         /// Name
@@ -232,7 +246,7 @@ public class TorrentInfo
         /// Peer limit
         /// </summary>
         [JsonProperty("peer-limit")]
-        public int PeerLimit { get; set; }
+        public long? PeerLimit { get; set; }
 
         /// <summary>
         /// Peers
@@ -244,7 +258,7 @@ public class TorrentInfo
         /// Peers connected
         /// </summary>
         [JsonProperty("peersConnected")]
-        public int PeersConnected { get; set; }
+        public long? PeersConnected { get; set; }
 
         /// <summary>
         /// Peers from
@@ -256,25 +270,25 @@ public class TorrentInfo
         /// Peers getting from us
         /// </summary>
         [JsonProperty("peersGettingFromUs")]
-        public int PeersGettingFromUs { get; set; }
+        public long? PeersGettingFromUs { get; set; }
 
         /// <summary>
         /// Peers sending to us
         /// </summary>
         [JsonProperty("peersSendingToUs")]
-        public int PeersSendingToUs { get; set; }
+        public long? PeersSendingToUs { get; set; }
 
         /// <summary>
         /// Percent complete
         /// </summary>
         [JsonProperty("percentComplete")]
-        public double PercentComplete { get; set; }
+        public double? PercentComplete { get; set; }
 
         /// <summary>
         /// Percent done
         /// </summary>
         [JsonProperty("percentDone")]
-        public double PercentDone { get; set; }
+        public double? PercentDone { get; set; }
 
         /// <summary>
         /// Pieces
@@ -286,19 +300,19 @@ public class TorrentInfo
         /// Piece count
         /// </summary>
         [JsonProperty("pieceCount")]
-        public int PieceCount { get; set; }
+        public long? PieceCount { get; set; }
 
         /// <summary>
         /// Piece size
         /// </summary>
         [JsonProperty("pieceSize")]
-        public long PieceSize { get; set; }
+        public long? PieceSize { get; set; }
 
         /// <summary>
         /// Priorities
         /// </summary>
         [JsonProperty("priorities")]
-        public int[] Priorities { get; set; }
+        public long?[] Priorities { get; set; }
 
         /// <summary>
         /// Primary mime type
@@ -310,79 +324,85 @@ public class TorrentInfo
         /// Queue position
         /// </summary>
         [JsonProperty("queuePosition")]
-        public int QueuePosition { get; set; }
+        public long? QueuePosition { get; set; }
 
         /// <summary>
         /// Rate download
         /// </summary>
         [JsonProperty("rateDownload")]
-        public int RateDownload { get; set; }
+        public long? RateDownload { get; set; }
 
         /// <summary>
         /// Rate upload
         /// </summary>
         [JsonProperty("rateUpload")]
-        public int RateUpload { get; set; }
+        public long? RateUpload { get; set; }
 
         /// <summary>
         /// Recheck progress
         /// </summary>
         [JsonProperty("recheckProgress")]
-        public double RecheckProgress { get; set; }
+        public double? RecheckProgress { get; set; }
 
         /// <summary>
         /// Seconds downloading
         /// </summary>
         [JsonProperty("secondsDownloading")]
-        public int SecondsDownloading { get; set; }
+        public long? SecondsDownloading { get; set; }
 
         /// <summary>
         /// Seconds seeding
         /// </summary>
         [JsonProperty("secondsSeeding")]
-        public int SecondsSeeding { get; set; }
+        public long? SecondsSeeding { get; set; }
 
         /// <summary>
         /// Seed idle limit
         /// </summary>
         [JsonProperty("seedIdleLimit")]
-        public int SeedIdleLimit { get; set; }
+        public long? SeedIdleLimit { get; set; }
 
         /// <summary>
         /// Seed idle mode
         /// </summary>
         [JsonProperty("seedIdleMode")]
-        public int SeedIdleMode { get; set; }
+        public long? SeedIdleMode { get; set; }
 
         /// <summary>
         /// Seed ratio limit
         /// </summary>
         [JsonProperty("seedRatioLimit")]
-        public double SeedRatioLimit { get; set; }
+        public double? SeedRatioLimit { get; set; }
 
         /// <summary>
         /// Seed ratio mode
         /// </summary>
         [JsonProperty("seedRatioMode")]
-        public int SeedRatioMode { get; set; }
+        public long? SeedRatioMode { get; set; }
+
+        /// <summary>
+        /// Sequential Download
+        /// </summary>
+        [JsonProperty("sequentialDownload")]
+        public bool? SequentialDownload { get; set; }
 
         /// <summary>
         /// Size when done
         /// </summary>
         [JsonProperty("sizeWhenDone")]
-        public long SizeWhenDone { get; set; }
+        public long? SizeWhenDone { get; set; }
 
         /// <summary>
         /// Start date
         /// </summary>
         [JsonProperty("startDate")]
-        public long StartDate { get; set; }
+        public long? StartDate { get; set; }
 
         /// <summary>
         /// Status
         /// </summary>
         [JsonProperty("status")]
-        public int Status { get; set; }
+        public long? Status { get; set; }
 
         /// <summary>
         /// Trackers
@@ -408,7 +428,7 @@ public class TorrentInfo
         /// Total size
         /// </summary>
         [JsonProperty("totalSize")]
-        public long TotalSize { get; set; }
+        public long? TotalSize { get; set; }
 
         /// <summary>
         /// Torrent file
@@ -420,31 +440,31 @@ public class TorrentInfo
         /// Uploaded ever
         /// </summary>
         [JsonProperty("uploadedEver")]
-        public long UploadedEver { get; set; }
+        public long? UploadedEver { get; set; }
 
         /// <summary>
         /// Upload limit
         /// </summary>
         [JsonProperty("uploadLimit")]
-        public int UploadLimit { get; set; }
+        public long? UploadLimit { get; set; }
 
         /// <summary>
         /// Upload limited
         /// </summary>
         [JsonProperty("uploadLimited")]
-        public bool UploadLimited { get; set; }
+        public bool? UploadLimited { get; set; }
 
         /// <summary>
         /// Upload ratio
         /// </summary>
         [JsonProperty("uploadRatio")]
-        public double uploadRatio { get; set; }
+        public double? uploadRatio { get; set; }
 
         /// <summary>
         /// Wanted
         /// </summary>
         [JsonProperty("wanted")]
-        public bool[] Wanted { get; set; }
+        public bool?[] Wanted { get; set; }
 
         /// <summary>
         /// Web seeds
@@ -456,7 +476,7 @@ public class TorrentInfo
         /// Web seeds sending to us
         /// </summary>
         [JsonProperty("webseedsSendingToUs")]
-        public int WebseedsSendingToUs { get; set; }
+        public long? WebseedsSendingToUs { get; set; }
     }
 
     /// <summary>
@@ -468,19 +488,31 @@ public class TransmissionTorrentFiles
         /// Bytes completed
         /// </summary>
         [JsonProperty("bytesCompleted")]
-        public double BytesCompleted{ get; set; }
+        public long? BytesCompleted{ get; set; }
 
         /// <summary>
         /// Length
         /// </summary>
         [JsonProperty("length")]
-        public double Length{ get; set; }
+        public long? Length{ get; set; }
 
         /// <summary>
         /// Name
         /// </summary>
         [JsonProperty("name")]
         public string Name{ get; set; }
+
+        /// <summary>
+        /// First piece index of file
+        /// </summary>
+        [JsonProperty("beginPiece")]
+        public long? BeginPiece { get; set; }
+
+        /// <summary>
+        /// Last piece index of file
+        /// </summary>
+        [JsonProperty("endPiece")]
+        public long? EndPiece { get; set; }
     }
 
     /// <summary>
@@ -492,19 +524,19 @@ public class TransmissionTorrentFileStats
         /// Bytes completed
         /// </summary>
         [JsonProperty("bytesCompleted")]
-        public double BytesCompleted{ get; set; }
+        public long? BytesCompleted{ get; set; }
 
         /// <summary>
         /// Wanted
         /// </summary>
         [JsonProperty("wanted")]
-        public bool Wanted{ get; set; }
+        public bool? Wanted{ get; set; }
 
         /// <summary>
         /// Priority
         /// </summary>
         [JsonProperty("priority")]
-        public int Priority{ get; set; }
+        public long? Priority{ get; set; }
     }
 
     /// <summary>
@@ -528,13 +560,13 @@ public class TransmissionTorrentPeers
         /// Client is choked
         /// </summary>
         [JsonProperty("clientIsChoked")]
-        public bool ClientIsChoked{ get; set; }
+        public bool? ClientIsChoked{ get; set; }
 
         /// <summary>
-        /// Client is interested
+        /// Client is Interested
         /// </summary>
         [JsonProperty("clientIsInterested")]
-        public bool ClientIsInterested{ get; set; }
+        public bool? ClientIsInterested{ get; set; }
 
         /// <summary>
         /// Flag string
@@ -546,61 +578,67 @@ public class TransmissionTorrentPeers
         /// Is downloading from
         /// </summary>
         [JsonProperty("isDownloadingFrom")]
-        public bool IsDownloadingFrom{ get; set; }
+        public bool? IsDownloadingFrom{ get; set; }
 
         /// <summary>
         /// Is encrypted
         /// </summary>
         [JsonProperty("isEncrypted")]
-        public bool IsEncrypted{ get; set; }
+        public bool? IsEncrypted { get; set; }
+
+        /// <summary>
+        /// Is incoming
+        /// </summary>
+        [JsonProperty("isIncoming")]
+        public bool? IsIncoming { get; set; }
 
         /// <summary>
         /// Is uploading to
         /// </summary>
         [JsonProperty("isUploadingTo")]
-        public bool IsUploadingTo{ get; set; }
+        public bool? IsUploadingTo{ get; set; }
 
         /// <summary>
         /// Is UTP
         /// </summary>
         [JsonProperty("isUTP")]
-        public bool IsUTP{ get; set; }
+        public bool? IsUtp{ get; set; }
 
         /// <summary>
         /// Peer is choked
         /// </summary>
         [JsonProperty("peerIsChoked")]
-        public bool PeerIsChoked{ get; set; }
+        public bool? PeerIsChoked{ get; set; }
 
         /// <summary>
-        /// Peer is interested
+        /// Peer is Interested
         /// </summary>
         [JsonProperty("peerIsInterested")]
-        public bool PeerIsInterested{ get; set; }
+        public bool? PeerIsInterested{ get; set; }
 
         /// <summary>
         /// Port
         /// </summary>
         [JsonProperty("port")]
-        public int Port{ get; set; }
+        public long? Port{ get; set; }
 
         /// <summary>
         /// Progress
         /// </summary>
         [JsonProperty("progress")]
-        public double Progress{ get; set; }
+        public double? Progress{ get; set; }
 
         /// <summary>
         /// Rate to client
         /// </summary>
         [JsonProperty("rateToClient")]
-        public int RateToClient{ get; set; }
+        public long? RateToClient{ get; set; }
 
         /// <summary>
         /// Rate to peer
         /// </summary>
         [JsonProperty("rateToPeer")]
-        public int RateToPeer{ get; set; }
+        public long? RateToPeer{ get; set; }
     }
 
     /// <summary>
@@ -608,41 +646,47 @@ public class TransmissionTorrentPeers
     /// </summary>
     public class TransmissionTorrentPeersFrom
     {
+        /// <summary>
+        /// From cache
+        /// </summary>
+        [JsonProperty("fromCache")]
+        public long? FromCache { get; set; }
+
         /// <summary>
         /// From DHT
         /// </summary>
         [JsonProperty("fromDht")]
-        public int FromDHT{ get; set; }
+        public long? FromDht { get; set; }
 
         /// <summary>
         /// From incoming
         /// </summary>
         [JsonProperty("fromIncoming")]
-        public int FromIncoming{ get; set; }
+        public long? FromIncoming{ get; set; }
 
         /// <summary>
         /// From LPD
         /// </summary>
         [JsonProperty("fromLpd")]
-        public int FromLPD{ get; set; }
+        public long? FromLpd{ get; set; }
 
         /// <summary>
         /// From LTEP
         /// </summary>
         [JsonProperty("fromLtep")]
-        public int FromLTEP{ get; set; }
+        public long? FromLtep{ get; set; }
 
         /// <summary>
         /// From PEX
         /// </summary>
         [JsonProperty("fromPex")]
-        public int FromPEX{ get; set; }
+        public long? FromPex{ get; set; }
 
         /// <summary>
         /// From tracker
         /// </summary>
         [JsonProperty("fromTracker")]
-        public int FromTracker{ get; set; }
+        public long? FromTracker{ get; set; }
     }
 
     /// <summary>
@@ -654,13 +698,13 @@ public class TransmissionTorrentTrackers
         /// Announce
         /// </summary>
         [JsonProperty("announce")]
-        public string announce{ get; set; }
+        public string Announce{ get; set; }
 
         /// <summary>
         /// Id
         /// </summary>
         [JsonProperty("id")]
-        public int ID{ get; set; }
+        public long? Id{ get; set; }
 
         /// <summary>
         /// Scrape
@@ -668,11 +712,17 @@ public class TransmissionTorrentTrackers
         [JsonProperty("scrape")]
         public string Scrape{ get; set; }
 
+        /// <summary>
+        /// Site name
+        /// </summary>
+        [JsonProperty("sitename")]
+        public string SiteName { get; set; }
+
         /// <summary>
         /// Tier
         /// </summary>
         [JsonProperty("tier")]
-        public int Tier{ get; set; }
+        public long? Tier{ get; set; }
     }
 
     /// <summary>
@@ -684,31 +734,31 @@ public class TransmissionTorrentTrackerStats
         /// Announce
         /// </summary>
         [JsonProperty("announce")]
-        public string announce{ get; set; }
+        public string Announce{ get; set; }
 
         /// <summary>
         /// Announce state
         /// </summary>
         [JsonProperty("announceState")]
-        public int AnnounceState{ get; set; }
+        public long? AnnounceState{ get; set; }
 
         /// <summary>
         /// Download count
         /// </summary>
         [JsonProperty("downloadCount")]
-        public int DownloadCount{ get; set; }
+        public long? DownloadCount{ get; set; }
 
         /// <summary>
         /// Has announced
         /// </summary>
         [JsonProperty("hasAnnounced")]
-        public bool HasAnnounced{ get; set; }
+        public bool? HasAnnounced{ get; set; }
 
         /// <summary>
         /// Has scraped
         /// </summary>
         [JsonProperty("hasScraped")]
-        public bool HasScraped{ get; set; }
+        public bool? HasScraped{ get; set; }
 
         /// <summary>
         /// Host
@@ -720,19 +770,19 @@ public class TransmissionTorrentTrackerStats
         /// Is backup
         /// </summary>
         [JsonProperty("isBackup")]
-        public bool IsBackup{ get; set; }
+        public bool? IsBackup{ get; set; }
 
         /// <summary>
         /// Last announce peer count
         /// </summary>
         [JsonProperty("lastAnnouncePeerCount")]
-        public int LastAnnouncePeerCount{ get; set; }
+        public long? LastAnnouncePeerCount{ get; set; }
 
         /// <summary>
         /// Id
         /// </summary>
         [JsonProperty("id")]
-        public int ID{ get; set; }
+        public long? Id{ get; set; }
 
         /// <summary>
         /// Last announce result 
@@ -744,13 +794,13 @@ public class TransmissionTorrentTrackerStats
         /// Last announce succeeded
         /// </summary>
         [JsonProperty("lastAnnounceSucceeded")]
-        public bool LastAnnounceSucceeded{ get; set; }
+        public bool? LastAnnounceSucceeded{ get; set; }
 
         /// <summary>
         /// Last announce start time
         /// </summary>
         [JsonProperty("lastAnnounceStartTime")]
-        public int LastAnnounceStartTime{ get; set; }
+        public long? LastAnnounceStartTime{ get; set; }
 
         /// <summary>
         /// Last scrape result
@@ -762,37 +812,37 @@ public class TransmissionTorrentTrackerStats
         /// Last announce timed out
         /// </summary>
         [JsonProperty("lastAnnounceTimedOut")]
-        public bool LastAnnounceTimedOut{ get; set; }
+        public bool? LastAnnounceTimedOut{ get; set; }
 
         /// <summary>
         /// Last announce time
         /// </summary>
         [JsonProperty("lastAnnounceTime")]
-        public int LastAnnounceTime{ get; set; }
+        public long? LastAnnounceTime{ get; set; }
 
         /// <summary>
         /// Last scrape scceeded
         /// </summary>
         [JsonProperty("lastScrapeSucceeded")]
-        public bool LastScrapeSucceeded{ get; set; }
+        public bool? LastScrapeSucceeded{ get; set; }
 
         /// <summary>
         /// Last scrape start time
         /// </summary>
         [JsonProperty("lastScrapeStartTime")]
-        public int LastScrapeStartTime{ get; set; }
+        public long? LastScrapeStartTime{ get; set; }
 
         /// <summary>
         /// Last scrape timed out
         /// </summary>
         [JsonProperty("lastScrapeTimedOut")]
-        public bool LastScrapeTimedOut{ get; set; }
+        public bool? LastScrapeTimedOut{ get; set; }
 
         /// <summary>
         /// Last scrape time
         /// </summary>
         [JsonProperty("lastScrapeTime")]
-        public int LastScrapeTime{ get; set; }
+        public long? LastScrapeTime{ get; set; }
 
         /// <summary>
         /// Scrape
@@ -804,37 +854,43 @@ public class TransmissionTorrentTrackerStats
         /// Tier
         /// </summary>
         [JsonProperty("tier")]
-        public int Tier{ get; set; }
+        public long? Tier{ get; set; }
 
         /// <summary>
         /// Leecher count
         /// </summary>
         [JsonProperty("leecherCount")]
-        public int LeecherCount{ get; set; }
+        public long? LeecherCount{ get; set; }
 
         /// <summary>
         /// Next announce time
         /// </summary>
         [JsonProperty("nextAnnounceTime")]
-        public int NextAnnounceTime{ get; set; }
+        public long? NextAnnounceTime{ get; set; }
 
         /// <summary>
         /// Next scrape time
         /// </summary>
         [JsonProperty("nextScrapeTime")]
-        public int NextScrapeTime{ get; set; }
+        public long? NextScrapeTime{ get; set; }
 
         /// <summary>
         /// Scrape state
         /// </summary>
         [JsonProperty("scrapeState")]
-        public int ScrapeState{ get; set; }
+        public long? ScrapeState{ get; set; }
 
         /// <summary>
         /// Seeder count
         /// </summary>
         [JsonProperty("seederCount")]
-        public int SeederCount{ get; set; }
+        public long? SeederCount{ get; set; }
+
+        /// <summary>
+        /// Site name
+        /// </summary>
+        [JsonProperty("sitename")]
+        public string SiteName { get; set; }
     }
 
     /// <summary>
diff --git a/Transmission.API.RPC/Entity/Units.cs b/Transmission.API.RPC/Entity/Units.cs
index 50d402c..18aa269 100644
--- a/Transmission.API.RPC/Entity/Units.cs
+++ b/Transmission.API.RPC/Entity/Units.cs
@@ -22,7 +22,7 @@ public class Units
         /// Speed bytes
         /// </summary>
         [JsonProperty("speed-bytes")]
-        public int? SpeedBytes { get; set; }
+        public long? SpeedBytes { get; set; }
 
         /// <summary>
         /// Size units
@@ -34,7 +34,7 @@ public class Units
         /// Size bytes
         /// </summary>
         [JsonProperty("size-bytes")]
-        public int? SizeBytes { get; set; }
+        public long? SizeBytes { get; set; }
 
         /// <summary>
         /// Memory units
@@ -46,6 +46,6 @@ public class Units
         /// Memory bytes
         /// </summary>
         [JsonProperty("memory-bytes")]
-        public int? MemoryBytes { get; set; }
+        public long? MemoryBytes { get; set; }
     }
 }
diff --git a/Transmission.API.RPC/ITransmissionClient.cs b/Transmission.API.RPC/ITransmissionClient.cs
index b978ca0..fa0dbbc 100644
--- a/Transmission.API.RPC/ITransmissionClient.cs
+++ b/Transmission.API.RPC/ITransmissionClient.cs
@@ -1,4 +1,6 @@
-using Transmission.API.RPC.Arguments;
+using System.Threading.Tasks;
+using System;
+using Transmission.API.RPC.Arguments;
 using Transmission.API.RPC.Entity;
 
 namespace Transmission.API.RPC
@@ -38,7 +40,7 @@ public interface ITransmissionClient
         /// Get free space is available in a client-specified folder.
         /// </summary>
         /// <param name="path">The directory to query</param>
-        long FreeSpace(string path);
+        FreeSpace FreeSpace(string path);
 
         /// <summary>
         /// Get information of current session (API: session-get)
@@ -46,6 +48,13 @@ public interface ITransmissionClient
         /// <returns>Session information</returns>
         SessionInfo GetSessionInformation();
 
+        /// <summary>
+        /// Get information of current session (API: session-get)
+        /// </summary>
+		/// <param name="fields">Optional fields of session information</param>
+        /// <returns>Session information</returns>
+        SessionInfo GetSessionInformation(string[] fields);
+
         /// <summary>
         /// Get session stat
         /// </summary>
@@ -55,8 +64,8 @@ public interface ITransmissionClient
         /// <summary>
         /// See if your incoming peer port is accessible from the outside world (API: port-test)
         /// </summary>
-        /// <returns>Accessible state</returns>
-        bool PortTest();
+        /// <returns>A Tuple with a boolean of whether the port test succeeded, and a PortTestProtocol enum of which protocol was used for the test</returns>
+        Tuple<bool, PortTestProtocol> PortTest();
 
         /// <summary>
         /// Set information to current session (API: session-set)
@@ -174,5 +183,35 @@ public interface ITransmissionClient
         /// </summary>
         /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
         void TorrentVerify(object[] ids);
+
+        /// <summary>
+        /// Reannounce recently active torrents (API: torrent-reannounce)
+        /// </summary>
+        void TorrentReannounce();
+
+        /// <summary>
+        /// Reannounce torrents (API: torrent-reannounce)
+        /// </summary>
+        /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
+        void TorrentReannounce(object[] ids);
+
+        /// <summary>
+        /// Get bandwidth groups (API: group-get)
+        /// </summary>
+        /// <returns></returns>
+        BandwidthGroup[] BandwidthGroupGet();
+
+        /// <summary>
+        /// Get bandwidth groups (API: group-get)
+        /// </summary>
+        /// <param name="groups">Optional names of groups to get</param>
+        /// <returns></returns>
+        BandwidthGroup[] BandwidthGroupGet(string[] groups);
+
+        /// <summary>
+        /// Set bandwidth groups (API: group-set)
+        /// </summary>
+        /// <param name="group">A bandwidth group to set</param>
+        void BandwidthGroupSet(BandwidthGroupSettings group);
     }
 }
diff --git a/Transmission.API.RPC/ITransmissionClientAsync.cs b/Transmission.API.RPC/ITransmissionClientAsync.cs
index dbf91c6..a341752 100644
--- a/Transmission.API.RPC/ITransmissionClientAsync.cs
+++ b/Transmission.API.RPC/ITransmissionClientAsync.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
 using Transmission.API.RPC.Arguments;
 using Transmission.API.RPC.Entity;
 
@@ -25,14 +26,22 @@ public interface ITransmissionClientAsync
         /// Get free space is available in a client-specified folder.
         /// </summary>
         /// <param name="path">The directory to query</param>
-        Task<long> FreeSpaceAsync(string path);
+        Task<FreeSpace> FreeSpaceAsync(string path);
 
         /// <summary>
         /// Get information of current session (API: session-get)
         /// </summary>
+		/// <param name="fields">Fields of session information</param>
         /// <returns>Session information</returns>
         Task<SessionInfo> GetSessionInformationAsync();
 
+        /// <summary>
+        /// Get information of current session (API: session-get)
+        /// </summary>
+		/// <param name="fields">Optional fields of session information</param>
+        /// <returns>Session information</returns>
+        Task<SessionInfo> GetSessionInformationAsync(string[] fields);
+
         /// <summary>
         /// Get session stat
         /// </summary>
@@ -42,8 +51,8 @@ public interface ITransmissionClientAsync
         /// <summary>
         /// See if your incoming peer port is accessible from the outside world (API: port-test)
         /// </summary>
-        /// <returns>Accessible state</returns>
-        Task<bool> PortTestAsync();
+        /// <returns>A Tuple with a boolean of whether the port test succeeded, and a PortTestProtocol enum of which protocol was used for the test</returns>
+        Task<Tuple<bool, PortTestProtocol>> PortTestAsync();
 
         /// <summary>
         /// Set information to current session (API: session-set)
@@ -161,5 +170,35 @@ public interface ITransmissionClientAsync
         /// </summary>
         /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
         Task TorrentVerifyAsync(object[] ids);
+
+        /// <summary>
+        /// Reannounce recently active torrents (API: torrent-reannounce)
+        /// </summary>
+        Task TorrentReannounceAsync();
+
+        /// <summary>
+        /// Reannounce torrents (API: torrent-reannounce)
+        /// </summary>
+        /// <param name="ids">A list of torrent id numbers, sha1 hash strings, or both</param>
+        Task TorrentReannounceAsync(object[] ids);
+
+        /// <summary>
+        /// Get bandwidth groups (API: group-get)
+        /// </summary>
+        /// <returns></returns>
+        Task<BandwidthGroup[]> BandwidthGroupGetAsync();
+
+        /// <summary>
+        /// Get bandwidth groups (API: group-get)
+        /// </summary>
+        /// <param name="groups">Optional names of groups to get</param>
+        /// <returns></returns>
+        Task<BandwidthGroup[]> BandwidthGroupGetAsync(string[] groups);
+
+        /// <summary>
+        /// Set bandwidth groups (API: group-set)
+        /// </summary>
+        /// <param name="group">A bandwidth group to set</param>
+        Task BandwidthGroupSetAsync(BandwidthGroupSettings group);
     }
 }
diff --git a/Transmission.API.RPC/Transmission.API.RPC.csproj b/Transmission.API.RPC/Transmission.API.RPC.csproj
index f020772..680474b 100644
--- a/Transmission.API.RPC/Transmission.API.RPC.csproj
+++ b/Transmission.API.RPC/Transmission.API.RPC.csproj
@@ -25,7 +25,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
+    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
     <PackageReference Include="System.Net.Http" Version="4.3.4" />
     <PackageReference Include="System.Net.Requests" Version="4.3.0" />
   </ItemGroup>

From e1900ca6e7f67335bec49d1bb8e7ab5050556475 Mon Sep 17 00:00:00 2001
From: Jdbye <jdbye3@gmail.com>
Date: Sun, 9 Jun 2024 22:51:23 +0200
Subject: [PATCH 3/5] Corrected "removed" field of torrent-get, renamed some
 functions for clarity, added TorrentGetRecentlyActive

Also added missing summaries to get rid of those pesky warnings from Visual Studio
---
 .../Arguments/BandwidthGroupSettings.cs       |   3 +
 .../Arguments/SessionFields.cs                | 174 +++++++++++++++++-
 Transmission.API.RPC/Client.Async.cs          |  31 +++-
 Transmission.API.RPC/Client.cs                |  32 +++-
 Transmission.API.RPC/Entity/BandwidthGroup.cs |   3 +
 Transmission.API.RPC/Entity/TorrentInfo.cs    |   2 +-
 Transmission.API.RPC/ITransmissionClient.cs   |  17 +-
 .../ITransmissionClientAsync.cs               |  18 +-
 8 files changed, 250 insertions(+), 30 deletions(-)

diff --git a/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs b/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs
index 4d22b54..b7096f4 100644
--- a/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs
+++ b/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs
@@ -5,6 +5,9 @@
 
 namespace Transmission.API.RPC.Arguments
 {
+    /// <summary>
+    /// Bandwidth group settings for editing bandwidth groups
+    /// </summary>
     public class BandwidthGroupSettings : ArgumentsBase
     {
         /// <summary>
diff --git a/Transmission.API.RPC/Arguments/SessionFields.cs b/Transmission.API.RPC/Arguments/SessionFields.cs
index b0855e8..5f095b5 100644
--- a/Transmission.API.RPC/Arguments/SessionFields.cs
+++ b/Transmission.API.RPC/Arguments/SessionFields.cs
@@ -10,64 +10,234 @@ namespace Transmission.API.RPC.Arguments
     public sealed class SessionFields
     {
         private SessionFields() { }
-
+        /// <summary>
+        /// alt-speed-down
+        /// </summary>
         public const string ALT_SPEED_DOWN = "alt-speed-down";
+        /// <summary>
+        /// alt-speed-enabled
+        /// </summary>
         public const string ALT_SPEED_ENABLED = "alt-speed-enabled";
+        /// <summary>
+        /// alt-speed-time-begin
+        /// </summary>
         public const string ALT_SPEED_TIME_BEGIN = "alt-speed-time-begin";
+        /// <summary>
+        /// alt-speed-time-enabled
+        /// </summary>
         public const string ALT_SPEED_TIME_ENABLED = "alt-speed-time-enabled";
+        /// <summary>
+        /// alt-speed-time-end
+        /// </summary>
         public const string ALT_SPEED_TIME_END = "alt-speed-time-end";
+        /// <summary>
+        /// alt-speed-time-day
+        /// </summary>
         public const string ALT_SPEED_TIME_DAY = "alt-speed-time-day";
+        /// <summary>
+        /// alt-speed-up
+        /// </summary>
         public const string ALT_SPEED_UP = "alt-speed-up";
+        /// <summary>
+        /// blocklist-url
+        /// </summary>
         public const string BLOCKLIST_URL = "blocklist-url";
+        /// <summary>
+        /// blocklist-enabled
+        /// </summary>
         public const string BLOCKLIST_ENABLED = "blocklist-enabled";
+        /// <summary>
+        /// blocklist-size
+        /// </summary>
         public const string BLOCKLIST_SIZE = "blocklist-size";
+        /// <summary>
+        /// cache-size-mb
+        /// </summary>
         public const string CACHE_SIZE_MB = "cache-size-mb";
+        /// <summary>
+        /// default-trackers
+        /// </summary>
         public const string DEFAULT_TRACKERS = "default-trackers";
+        /// <summary>
+        /// dht-enabled
+        /// </summary>
         public const string DHT_ENABLED = "dht-enabled";
+        /// <summary>
+        /// download-dir
+        /// </summary>
         public const string DOWNLOAD_DIR = "download-dir";
+        /// <summary>
+        /// download-dir-free-space
+        /// </summary>
         public const string DOWNLOAD_DIR_FREE_SPACE = "download-dir-free-space";
+        /// <summary>
+        /// download-queue-size
+        /// </summary>
         public const string DOWNLOAD_QUEUE_SIZE = "download-queue-size";
+        /// <summary>
+        /// download-queue-enabled
+        /// </summary>
         public const string DOWNLOAD_QUEUE_ENABLED = "download-queue-enabled";
+        /// <summary>
+        /// encryption
+        /// </summary>
         public const string ENCRYPTION = "encryption";
+        /// <summary>
+        /// idle-seeding-limit
+        /// </summary>
         public const string IDLE_SEEDING_LIMIT = "idle-seeding-limit";
+        /// <summary>
+        /// idle-seeding-limit-enabled
+        /// </summary>
         public const string IDLE_SEEDING_LIMIT_ENABLED = "idle-seeding-limit-enabled";
+        /// <summary>
+        /// incomplete-dir
+        /// </summary>
         public const string INCOMPLETE_DIR = "incomplete-dir";
+        /// <summary>
+        /// incomplete-dir-enabled
+        /// </summary>
         public const string INCOMPLETE_DIR_ENABLED = "incomplete-dir-enabled";
+        /// <summary>
+        /// lpd-enabled
+        /// </summary>
         public const string LPD_ENABLED = "lpd-enabled";
+        /// <summary>
+        /// peer-limit-global
+        /// </summary>
         public const string PEER_LIMIT_GLOBAL = "peer-limit-global";
+        /// <summary>
+        /// peer-limit-per-torrent
+        /// </summary>
         public const string PEER_LIMIT_PER_TORRENT = "peer-limit-per-torrent";
+        /// <summary>
+        /// pex-enabled
+        /// </summary>
         public const string PEX_ENABLED = "pex-enabled";
+        /// <summary>
+        /// peer-port
+        /// </summary>
         public const string PEER_PORT = "peer-port";
+        /// <summary>
+        /// peer-port-random-on-start
+        /// </summary>
         public const string PEER_PORT_RANDOM_ON_START = "peer-port-random-on-start";
+        /// <summary>
+        /// port-forwarding-enabled
+        /// </summary>
         public const string PORT_FORWARDING_ENABLED = "port-forwarding-enabled";
+        /// <summary>
+        /// queue-stalled-enabled
+        /// </summary>
         public const string QUEUE_STALLED_ENABLED = "queue-stalled-enabled";
+        /// <summary>
+        /// queue-stalled-minutes
+        /// </summary>
         public const string QUEUE_STALLED_MINUTES = "queue-stalled-minutes";
+        /// <summary>
+        /// rename-partial-files
+        /// </summary>
         public const string RENAME_PARTIAL_FILES = "rename-partial-files";
+        /// <summary>
+        /// session-id
+        /// </summary>
         public const string SESSION_ID = "session-id";
+        /// <summary>
+        /// script-torrent-added-filename
+        /// </summary>
         public const string SCRIPT_TORRENT_ADDED_FILENAME = "script-torrent-added-filename";
+        /// <summary>
+        /// script-torrent-added-enabled
+        /// </summary>
         public const string SCRIPT_TORRENT_ADDED_ENABLED = "script-torrent-added-enabled";
+        /// <summary>
+        /// script-torrent-done-filename
+        /// </summary>
         public const string SCRIPT_TORRENT_DONE_FILENAME = "script-torrent-done-filename";
+        /// <summary>
+        /// script-torrent-done-enabled
+        /// </summary>
         public const string SCRIPT_TORRENT_DONE_ENABLED = "script-torrent-done-enabled";
+        /// <summary>
+        /// script-torrent-done-seeding-filename
+        /// </summary>
         public const string SCRIPT_TORRENT_DONE_SEEDING_FILENAME = "script-torrent-done-seeding-filename";
+        /// <summary>
+        /// script-torrent-done-seeding-enabled
+        /// </summary>
         public const string SCRIPT_TORRENT_DONE_SEEDING_ENABLED = "script-torrent-done-seeding-enabled";
+        /// <summary>
+        /// seedRatioLimit
+        /// </summary>
         public const string SEED_RATIO_LIMIT = "seedRatioLimit";
+        /// <summary>
+        /// seedRatioLimited
+        /// </summary>
         public const string SEED_RATIO_LIMITED = "seedRatioLimited";
+        /// <summary>
+        /// seed-queue-size
+        /// </summary>
         public const string SEED_QUEUE_SIZE = "seed-queue-size";
+        /// <summary>
+        /// seed-queue-enabled
+        /// </summary>
         public const string SEED_QUEUE_ENABLED = "seed-queue-enabled";
+        /// <summary>
+        /// speed-limit-down
+        /// </summary>
         public const string SPEED_LIMIT_DOWN = "speed-limit-down";
+        /// <summary>
+        /// speed-limit-down-enabled
+        /// </summary>
         public const string SPEED_LIMIT_DOWN_ENABLED = "speed-limit-down-enabled";
+        /// <summary>
+        /// speed-limit-up
+        /// </summary>
         public const string SPEED_LIMIT_UP = "speed-limit-up";
+        /// <summary>
+        /// speed-limit-up-enabled
+        /// </summary>
         public const string SPEED_LIMIT_UP_ENABLED = "speed-limit-up-enabled";
+        /// <summary>
+        /// start-added-torrents
+        /// </summary>
         public const string START_ADDED_TORRENTS = "start-added-torrents";
+        /// <summary>
+        /// trash-original-torrent-files
+        /// </summary>
         public const string TRASH_ORIGINAL_TORRENT_FILES = "trash-original-torrent-files";
+        /// <summary>
+        /// units
+        /// </summary>
         public const string UNITS = "units";
+        /// <summary>
+        /// utp-enabled
+        /// </summary>
         public const string UTP_ENABLED = "utp-enabled";
+        /// <summary>
+        /// config-dir
+        /// </summary>
         public const string CONFIG_DIR = "config-dir";
+        /// <summary>
+        /// rpc-version
+        /// </summary>
         public const string RPC_VERSION = "rpc-version";
+        /// <summary>
+        /// rpc-version-minimum
+        /// </summary>
         public const string RPC_VERSION_MINIMUM = "rpc-version-minimum";
+        /// <summary>
+        /// rpc-version-semver
+        /// </summary>
         public const string RPC_VERSION_SEMVER = "rpc-version-semver";
+        /// <summary>
+        /// version
+        /// </summary>
         public const string VERSION = "version";
 
+        /// <summary>
+        /// All fields
+        /// </summary>
         public static string[] ALL_FIELDS
         {
             get
@@ -89,7 +259,7 @@ public static string[] ALL_FIELDS
                     DEFAULT_TRACKERS,
                     DHT_ENABLED,
                     DOWNLOAD_DIR,
-                    DOWNLOAD_DIR_FREE_SPACE,
+                    //DOWNLOAD_DIR_FREE_SPACE, // Deprecated
                     DOWNLOAD_QUEUE_SIZE,
                     DOWNLOAD_QUEUE_ENABLED,
                     ENCRYPTION,
diff --git a/Transmission.API.RPC/Client.Async.cs b/Transmission.API.RPC/Client.Async.cs
index 14aba55..fbcbd3c 100644
--- a/Transmission.API.RPC/Client.Async.cs
+++ b/Transmission.API.RPC/Client.Async.cs
@@ -126,6 +126,25 @@ public async Task TorrentSetAsync(TorrentSettings settings)
             var response = await SendRequestAsync(request);
         }
 
+        /// <summary>
+        /// Get fields of recently active torrents (API: torrent-get)
+        /// </summary>
+        /// <param name="fields">Fields of torrents</param>
+        /// <returns>Torrents info</returns>
+        public async Task<TransmissionTorrents> TorrentGetRecentlyActiveAsync(string[] fields)
+        {
+            var arguments = new Dictionary<string, object>();
+            arguments.Add("fields", fields);
+            arguments.Add("ids", "recently-active");
+
+            var request = new TransmissionRequest("torrent-get", arguments);
+
+            var response = await SendRequestAsync(request);
+            var result = response.Deserialize<TransmissionTorrents>();
+
+            return result;
+        }
+
         /// <summary>
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
@@ -143,7 +162,7 @@ public async Task<TransmissionTorrents> TorrentGetAsync(string[] fields, params
             var request = new TransmissionRequest("torrent-get", arguments);
 
             var response = await SendRequestAsync(request);
-            var json = response.ToJson
+            var result = response.Deserialize<TransmissionTorrents>();
 
             return result;
         }
@@ -179,7 +198,7 @@ public async Task TorrentStartAsync(object[] ids)
         /// <summary>
         /// Start recently active torrents (API: torrent-start)
         /// </summary>
-        public async Task TorrentStartAsync()
+        public async Task TorrentStartRecentlyActiveAsync()
         {
             var request = new TransmissionRequest("torrent-start", new Dictionary<string, object> { { "ids", "recently-active" } });
             var response = await SendRequestAsync(request);
@@ -202,7 +221,7 @@ public async Task TorrentStartNowAsync(object[] ids)
         /// <summary>
         /// Start now recently active torrents (API: torrent-start-now)
         /// </summary>
-        public async Task TorrentStartNowAsync()
+        public async Task TorrentStartNowRecentlyActiveAsync()
         {
             var request = new TransmissionRequest("torrent-start-now", new Dictionary<string, object> { { "ids", "recently-active" } });
             var response = await SendRequestAsync(request);
@@ -225,7 +244,7 @@ public async Task TorrentStopAsync(object[] ids)
         /// <summary>
         /// Stop recently active torrents (API: torrent-stop)
         /// </summary>
-        public async Task TorrentStopAsync()
+        public async Task TorrentStopRecentlyActiveAsync()
         {
             var request = new TransmissionRequest("torrent-stop", new Dictionary<string, object> { { "ids", "recently-active" } });
             var response = await SendRequestAsync(request);
@@ -248,7 +267,7 @@ public async Task TorrentVerifyAsync(object[] ids)
         /// <summary>
         /// Verify recently active torrents (API: torrent-verify)
         /// </summary>
-        public async Task TorrentVerifyAsync()
+        public async Task TorrentVerifyRecentlyActiveAsync()
         {
             var request = new TransmissionRequest("torrent-verify", new Dictionary<string, object> { { "ids", "recently-active" } });
             var response = await SendRequestAsync(request);
@@ -271,7 +290,7 @@ public async Task TorrentReannounceAsync(object[] ids)
         /// <summary>
         /// Reannounce recently active torrents (API: torrent-reannounce)
         /// </summary>
-        public async Task TorrentReannounceAsync()
+        public async Task TorrentReannounceRecentlyActiveAsync()
         {
             var request = new TransmissionRequest("torrent-reannounce", new Dictionary<string, object> { { "ids", "recently-active" } });
             var response = await SendRequestAsync(request);
diff --git a/Transmission.API.RPC/Client.cs b/Transmission.API.RPC/Client.cs
index 46d2b2f..abb142d 100644
--- a/Transmission.API.RPC/Client.cs
+++ b/Transmission.API.RPC/Client.cs
@@ -146,6 +146,18 @@ public void TorrentSet(TorrentSettings settings)
             TorrentSetAsync(settings).WaitAndUnwrapException();
         }
 
+        /// <summary>
+        /// Get fields of recently active torrents (API: torrent-get)
+        /// </summary>
+        /// <param name="fields">Fields of torrents</param>
+        /// <returns>Torrents info</returns>
+        public TransmissionTorrents TorrentGetRecentlyActive(string[] fields)
+        {
+            var task = TorrentGetRecentlyActiveAsync(fields);
+            task.WaitAndUnwrapException();
+            return task.Result;
+        }
+
         /// <summary>
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
@@ -182,9 +194,9 @@ public void TorrentStart(object[] ids)
         /// <summary>
         /// Start recently active torrents (API: torrent-start)
         /// </summary>
-        public void TorrentStart()
+        public void TorrentStartRecentlyActive()
         {
-            TorrentStartAsync().WaitAndUnwrapException();
+            TorrentStartRecentlyActiveAsync().WaitAndUnwrapException();
         }
         #endregion
 
@@ -202,9 +214,9 @@ public void TorrentStartNow(object[] ids)
         /// <summary>
         /// Start now recently active torrents (API: torrent-start-now)
         /// </summary>
-        public void TorrentStartNow()
+        public void TorrentStartNowRecentlyActive()
         {
-            TorrentStartNowAsync().WaitAndUnwrapException();
+            TorrentStartNowRecentlyActiveAsync().WaitAndUnwrapException();
         }
         #endregion
 
@@ -221,9 +233,9 @@ public void TorrentStop(object[] ids)
         /// <summary>
         /// Stop recently active torrents (API: torrent-stop)
         /// </summary>
-        public void TorrentStop()
+        public void TorrentStopRecentlyActive()
         {
-            TorrentStopAsync().WaitAndUnwrapException();
+            TorrentStopRecentlyActiveAsync().WaitAndUnwrapException();
         }
         #endregion
 
@@ -241,9 +253,9 @@ public void TorrentVerify(object[] ids)
         /// <summary>
         /// Verify recently active torrents (API: torrent-verify)
         /// </summary>
-        public void TorrentVerify()
+        public void TorrentVerifyRecentlyActive()
         {
-            TorrentVerifyAsync().WaitAndUnwrapException();
+            TorrentVerifyRecentlyActiveAsync().WaitAndUnwrapException();
         }
 
         #endregion
@@ -262,9 +274,9 @@ public void TorrentReannounce(object[] ids)
         /// <summary>
         /// Reannounce recently active torrents (API: torrent-reannounce)
         /// </summary>
-        public void TorrentReannounce()
+        public void TorrentReannounceRecentlyActive()
         {
-            TorrentReannounceAsync().WaitAndUnwrapException();
+            TorrentReannounceRecentlyActiveAsync().WaitAndUnwrapException();
         }
 
         #endregion
diff --git a/Transmission.API.RPC/Entity/BandwidthGroup.cs b/Transmission.API.RPC/Entity/BandwidthGroup.cs
index 9d6df63..df48db6 100644
--- a/Transmission.API.RPC/Entity/BandwidthGroup.cs
+++ b/Transmission.API.RPC/Entity/BandwidthGroup.cs
@@ -5,6 +5,9 @@
 
 namespace Transmission.API.RPC.Entity
 {
+    /// <summary>
+    /// Contains settings for a bandwidth group
+    /// </summary>
     public class BandwidthGroup
     {
         /// <summary>
diff --git a/Transmission.API.RPC/Entity/TorrentInfo.cs b/Transmission.API.RPC/Entity/TorrentInfo.cs
index d7ac0e2..d18ee1e 100644
--- a/Transmission.API.RPC/Entity/TorrentInfo.cs
+++ b/Transmission.API.RPC/Entity/TorrentInfo.cs
@@ -908,6 +908,6 @@ public class TransmissionTorrents
         /// Array of torrent-id numbers of recently-removed torrents
         /// </summary>
         [JsonProperty("removed")]
-        public TorrentInfo[] Removed{ get; set; }
+        public long?[] Removed{ get; set; }
     }
 }
diff --git a/Transmission.API.RPC/ITransmissionClient.cs b/Transmission.API.RPC/ITransmissionClient.cs
index fa0dbbc..911dc62 100644
--- a/Transmission.API.RPC/ITransmissionClient.cs
+++ b/Transmission.API.RPC/ITransmissionClient.cs
@@ -79,6 +79,13 @@ public interface ITransmissionClient
         /// <returns>Torrent info (ID, Name and HashString)</returns>
         NewTorrentInfo TorrentAdd(NewTorrent torrent);
 
+        /// <summary>
+        /// Get fields of recently active torrents (API: torrent-get)
+        /// </summary>
+        /// <param name="fields">Fields of torrents</param>
+        /// <returns>Torrents info</returns>
+        TransmissionTorrents TorrentGetRecentlyActive(string[] fields);
+
         /// <summary>
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
@@ -143,7 +150,7 @@ public interface ITransmissionClient
         /// <summary>
         /// Start recently active torrents (API: torrent-start)
         /// </summary>
-        void TorrentStart();
+        void TorrentStartRecentlyActive();
 
         /// <summary>
         /// Start torrents (API: torrent-start)
@@ -154,7 +161,7 @@ public interface ITransmissionClient
         /// <summary>
         /// Start now recently active torrents (API: torrent-start-now)
         /// </summary>
-        void TorrentStartNow();
+        void TorrentStartNowRecentlyActive();
 
         /// <summary>
         /// Start now torrents (API: torrent-start-now)
@@ -165,7 +172,7 @@ public interface ITransmissionClient
         /// <summary>
         /// Stop recently active torrents (API: torrent-stop)
         /// </summary>
-        void TorrentStop();
+        void TorrentStopRecentlyActive();
 
         /// <summary>
         /// Stop torrents (API: torrent-stop)
@@ -176,7 +183,7 @@ public interface ITransmissionClient
         /// <summary>
         /// Verify recently active torrents (API: torrent-verify)
         /// </summary>
-        void TorrentVerify();
+        void TorrentVerifyRecentlyActive();
 
         /// <summary>
         /// Verify torrents (API: torrent-verify)
@@ -187,7 +194,7 @@ public interface ITransmissionClient
         /// <summary>
         /// Reannounce recently active torrents (API: torrent-reannounce)
         /// </summary>
-        void TorrentReannounce();
+        void TorrentReannounceRecentlyActive();
 
         /// <summary>
         /// Reannounce torrents (API: torrent-reannounce)
diff --git a/Transmission.API.RPC/ITransmissionClientAsync.cs b/Transmission.API.RPC/ITransmissionClientAsync.cs
index a341752..192bba7 100644
--- a/Transmission.API.RPC/ITransmissionClientAsync.cs
+++ b/Transmission.API.RPC/ITransmissionClientAsync.cs
@@ -31,7 +31,6 @@ public interface ITransmissionClientAsync
         /// <summary>
         /// Get information of current session (API: session-get)
         /// </summary>
-		/// <param name="fields">Fields of session information</param>
         /// <returns>Session information</returns>
         Task<SessionInfo> GetSessionInformationAsync();
 
@@ -66,6 +65,13 @@ public interface ITransmissionClientAsync
         /// <returns>Torrent info (ID, Name and HashString)</returns>
         Task<NewTorrentInfo> TorrentAddAsync(NewTorrent torrent);
 
+        /// <summary>
+        /// Get fields of recently active torrents (API: torrent-get)
+        /// </summary>
+        /// <param name="fields">Fields of torrents</param>
+        /// <returns>Torrents info</returns>
+        Task<TransmissionTorrents> TorrentGetRecentlyActiveAsync(string[] fields);
+
         /// <summary>
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
@@ -130,7 +136,7 @@ public interface ITransmissionClientAsync
         /// <summary>
         /// Start recently active torrents (API: torrent-start)
         /// </summary>
-        Task TorrentStartAsync();
+        Task TorrentStartRecentlyActiveAsync();
 
         /// <summary>
         /// Start torrents (API: torrent-start)
@@ -141,7 +147,7 @@ public interface ITransmissionClientAsync
         /// <summary>
         /// Start now recently active torrents (API: torrent-start-now)
         /// </summary>
-        Task TorrentStartNowAsync();
+        Task TorrentStartNowRecentlyActiveAsync();
 
         /// <summary>
         /// Start now torrents (API: torrent-start-now)
@@ -152,7 +158,7 @@ public interface ITransmissionClientAsync
         /// <summary>
         /// Stop recently active torrents (API: torrent-stop)
         /// </summary>
-        Task TorrentStopAsync();
+        Task TorrentStopRecentlyActiveAsync();
 
         /// <summary>
         /// Stop torrents (API: torrent-stop)
@@ -163,7 +169,7 @@ public interface ITransmissionClientAsync
         /// <summary>
         /// Verify recently active torrents (API: torrent-verify)
         /// </summary>
-        Task TorrentVerifyAsync();
+        Task TorrentVerifyRecentlyActiveAsync();
 
         /// <summary>
         /// Verify torrents (API: torrent-verify)
@@ -174,7 +180,7 @@ public interface ITransmissionClientAsync
         /// <summary>
         /// Reannounce recently active torrents (API: torrent-reannounce)
         /// </summary>
-        Task TorrentReannounceAsync();
+        Task TorrentReannounceRecentlyActiveAsync();
 
         /// <summary>
         /// Reannounce torrents (API: torrent-reannounce)

From f4e5e04794377635aefa019825bb65301b855876 Mon Sep 17 00:00:00 2001
From: Jdbye <jdbye3@gmail.com>
Date: Mon, 10 Jun 2024 02:44:29 +0200
Subject: [PATCH 4/5] More performance.

* Significantly more performant (around 20% when fetching a large amount of torrents with torrent-get)
* torrent-get now supports the table format. This by itself only provides a small speed improvement.
* RPC requests no longer need to deserialize->serialize->deserialize the JSON to arrive at the final result. Once is enough.
---
 Transmission.API.RPC.Test/MethodsTest.cs      |  20 +--
 .../Transmission.API.RPC.Test.csproj          |   2 +-
 .../Arguments/BandwidthGroupSettings.cs       |  10 +-
 Transmission.API.RPC/Arguments/NewTorrent.cs  |   6 +-
 .../Arguments/SessionSettings.cs              |  80 +++++-----
 .../Arguments/TorrentFields.cs                |   2 +-
 .../Arguments/TorrentSettings.cs              |  34 ++---
 Transmission.API.RPC/Client.Async.cs          | 126 +++++++++-------
 Transmission.API.RPC/Client.cs                |  30 ++--
 .../Common/CommunicateBase.cs                 |  46 ------
 .../Common/TransmissionRequest.cs             |  25 ++-
 .../Common/TransmissionResponse.cs            |  49 +++++-
 Transmission.API.RPC/Entity/BandwidthGroup.cs |   5 +-
 Transmission.API.RPC/Entity/FreeSpace.cs      |   5 +-
 .../Entity/IntOrArrayConverter.cs             |  11 +-
 Transmission.API.RPC/Entity/NewTorrentInfo.cs |  11 +-
 .../Entity/RenameTorrentInfo.cs               |   9 +-
 Transmission.API.RPC/Entity/SessionInfo.cs    |  31 ++--
 Transmission.API.RPC/Entity/Statistic.cs      |   7 +-
 Transmission.API.RPC/Entity/TorrentInfo.cs    |  85 +++++------
 .../Entity/TorrentInfoConverter.cs            | 142 ++++++++++++++++++
 Transmission.API.RPC/Entity/Units.cs          |   9 +-
 Transmission.API.RPC/Exceptions.cs            |  16 ++
 Transmission.API.RPC/ITransmissionClient.cs   |  43 +++---
 .../ITransmissionClientAsync.cs               |  41 ++---
 .../Transmission.API.RPC.csproj               |   2 +-
 26 files changed, 529 insertions(+), 318 deletions(-)
 delete mode 100644 Transmission.API.RPC/Common/CommunicateBase.cs
 create mode 100644 Transmission.API.RPC/Entity/TorrentInfoConverter.cs
 create mode 100644 Transmission.API.RPC/Exceptions.cs

diff --git a/Transmission.API.RPC.Test/MethodsTest.cs b/Transmission.API.RPC.Test/MethodsTest.cs
index ccf2cee..5f000d1 100644
--- a/Transmission.API.RPC.Test/MethodsTest.cs
+++ b/Transmission.API.RPC.Test/MethodsTest.cs
@@ -44,7 +44,7 @@ public void AddTorrent_Test()
             var newTorrentInfo = client.TorrentAdd(torrent);
 			
 			Assert.IsNotNull(newTorrentInfo);
-			Assert.IsTrue(newTorrentInfo.ID != 0);
+			Assert.IsTrue(newTorrentInfo.Id != 0);
         }
 
         [TestMethod]
@@ -59,7 +59,7 @@ public void AddTorrent_Magnet_Test()
             var newTorrentInfo = client.TorrentAdd(torrent);
 
             Assert.IsNotNull(newTorrentInfo);
-            Assert.IsTrue(newTorrentInfo.ID != 0);
+            Assert.IsTrue(newTorrentInfo.Id != 0);
         }
 
         [TestMethod]
@@ -84,13 +84,13 @@ public void SetTorrentSettings_Test()
             var trackerCount = torrentInfo.Trackers.Length;
 			TorrentSettings settings = new TorrentSettings()
 			{
-				IDs = new object[] { torrentInfo.HashString },
-				TrackerRemove = new int[] { trackerInfo.ID }
+				Ids = new object[] { torrentInfo.HashString },
+				TrackerRemove = new long[] { trackerInfo.Id }
 			};
 
 			client.TorrentSet(settings);
 
-			torrentsInfo = client.TorrentGet(TorrentFields.ALL_FIELDS, torrentInfo.ID);
+			torrentsInfo = client.TorrentGet(TorrentFields.ALL_FIELDS, false, torrentInfo.Id);
 			torrentInfo = torrentsInfo.Torrents.FirstOrDefault();
 
 			Assert.IsFalse(trackerCount == torrentInfo.Trackers.Length);
@@ -103,10 +103,10 @@ public void RenamePathTorrent_Test()
             var torrentInfo = torrentsInfo.Torrents.FirstOrDefault();
             Assert.IsNotNull(torrentInfo, "Torrent not found");
 
-            var result = client.TorrentRenamePath(torrentInfo.ID, torrentInfo.Files[0].Name, "test_" + torrentInfo.Files[0].Name);
+            var result = client.TorrentRenamePath(torrentInfo.Id, torrentInfo.Files[0].Name, "test_" + torrentInfo.Files[0].Name);
 
             Assert.IsNotNull(result, "Torrent not found");
-            Assert.IsTrue(result.ID != 0);
+            Assert.IsTrue(result.Id != 0);
         }
 
         [TestMethod]
@@ -116,11 +116,11 @@ public void RemoveTorrent_Test()
 			var torrentInfo = torrentsInfo.Torrents.FirstOrDefault();
 			Assert.IsNotNull(torrentInfo, "Torrent not found");
 
-			client.TorrentRemove(new int[] { torrentInfo.ID });
+			client.TorrentRemove(new long[] { torrentInfo.Id });
 
 			torrentsInfo = client.TorrentGet(TorrentFields.ALL_FIELDS);
 
-			Assert.IsFalse(torrentsInfo.Torrents.Any(t => t.ID == torrentInfo.ID));
+			Assert.IsFalse(torrentsInfo.Torrents.Any(t => t.Id == torrentInfo.Id));
 		}
 
         #endregion
@@ -142,7 +142,7 @@ public void ChangeSessionTest()
             var sessionInformation = client.GetSessionInformation();
 
 			//Save old speed limit up
-			var oldSpeedLimit = sessionInformation.SpeedLimitUp;
+			var oldSpeedLimit = (long)sessionInformation.SpeedLimitUp;
 
             //Set new session settings
 			client.SetSessionSettings(new SessionSettings() { SpeedLimitUp = 100 });
diff --git a/Transmission.API.RPC.Test/Transmission.API.RPC.Test.csproj b/Transmission.API.RPC.Test/Transmission.API.RPC.Test.csproj
index 818fab3..cb17e6e 100644
--- a/Transmission.API.RPC.Test/Transmission.API.RPC.Test.csproj
+++ b/Transmission.API.RPC.Test/Transmission.API.RPC.Test.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>    
-    <TargetFramework>netcoreapp2.0</TargetFramework>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs b/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs
index b7096f4..8bbabc1 100644
--- a/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs
+++ b/Transmission.API.RPC/Arguments/BandwidthGroupSettings.cs
@@ -13,7 +13,7 @@ public class BandwidthGroupSettings : ArgumentsBase
         /// <summary>
         /// Session limits are honored
         /// </summary>
-        public bool? HonorsSessionLimits { get { return GetValue<bool?>("honorsSessionLimits"); } set { this["honorsSessionLimits"] = value; } }
+        public bool HonorsSessionLimits { get { return GetValue<bool>("honorsSessionLimits"); } set { this["honorsSessionLimits"] = value; } }
 
         /// <summary>
         /// Name of the bandwidth group
@@ -23,21 +23,21 @@ public class BandwidthGroupSettings : ArgumentsBase
         /// <summary>
         /// Max global download speed of this bandwidth group (KBps)
         /// </summary>
-        public long? SpeedLimitDown { get { return GetValue<long?>("speed-limit-down"); } set { this["speed-limit-down"] = value; } }
+        public long SpeedLimitDown { get { return GetValue<long>("speed-limit-down"); } set { this["speed-limit-down"] = value; } }
 
         /// <summary>
         /// True means enabled
         /// </summary>
-        public bool? SpeedLimitDownEnabled { get { return GetValue<bool?>("speed-limit-down-enabled"); } set { this["speed-limit-down-enabled"] = value; } }
+        public bool SpeedLimitDownEnabled { get { return GetValue<bool>("speed-limit-down-enabled"); } set { this["speed-limit-down-enabled"] = value; } }
 
         /// <summary>
         /// Max global upload speed of this bandwidth group (KBps)
         /// </summary>
-        public long? SpeedLimitUp { get { return GetValue<long?>("speed-limit-up"); } set { this["speed-limit-up"] = value; } }
+        public long SpeedLimitUp { get { return GetValue<long>("speed-limit-up"); } set { this["speed-limit-up"] = value; } }
 
         /// <summary>
         /// True means enabled
         /// </summary>
-        public bool? SpeedLimitUpEnabled { get { return GetValue<bool?>("speed-limit-up-enabled"); } set { this["speed-limit-up-enabled"] = value; } }
+        public bool SpeedLimitUpEnabled { get { return GetValue<bool>("speed-limit-up-enabled"); } set { this["speed-limit-up-enabled"] = value; } }
     }
 }
diff --git a/Transmission.API.RPC/Arguments/NewTorrent.cs b/Transmission.API.RPC/Arguments/NewTorrent.cs
index f3cd710..0ebc44f 100644
--- a/Transmission.API.RPC/Arguments/NewTorrent.cs
+++ b/Transmission.API.RPC/Arguments/NewTorrent.cs
@@ -41,17 +41,17 @@ public class NewTorrent : ArgumentsBase
         /// <summary>
         /// if true, don't start the torrent
         /// </summary>
-        public bool? Paused { get { return GetValue<bool>("paused"); } set { this["paused"] = value; } }
+        public bool Paused { get { return GetValue<bool>("paused"); } set { this["paused"] = value; } }
 
         /// <summary>
         /// maximum number of peers
         /// </summary>
-        public long? PeerLimit { get { return GetValue<long?>("peer-limit"); } set { this["peer-limit"] = value; } }
+        public long PeerLimit { get { return GetValue<long>("peer-limit"); } set { this["peer-limit"] = value; } }
 
         /// <summary>
         /// Torrent's bandwidth priority
         /// </summary>
-        public long? BandwidthPriority { get { return GetValue<long?>("bandwidthPriority"); } set { this["bandwidthPriority"] = value; } }
+        public long BandwidthPriority { get { return GetValue<long>("bandwidthPriority"); } set { this["bandwidthPriority"] = value; } }
 
         /// <summary>
         /// Indices of file(s) to download
diff --git a/Transmission.API.RPC/Arguments/SessionSettings.cs b/Transmission.API.RPC/Arguments/SessionSettings.cs
index d442905..318ccf1 100644
--- a/Transmission.API.RPC/Arguments/SessionSettings.cs
+++ b/Transmission.API.RPC/Arguments/SessionSettings.cs
@@ -17,37 +17,37 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Max global download speed (KBps)
         /// </summary>
-        public long? AlternativeSpeedDown { get { return GetValue<long?>("alt-speed-down"); } set { this["alt-speed-down"] = value; } }
+        public long AlternativeSpeedDown { get { return GetValue<long>("alt-speed-down"); } set { this["alt-speed-down"] = value; } }
 
         /// <summary>
         /// True means use the alt speeds
         /// </summary>
-        public bool? AlternativeSpeedEnabled { get { return GetValue<bool?>("alt-speed-enabled"); } set { this["alt-speed-enabled"] = value; } }
+        public bool AlternativeSpeedEnabled { get { return GetValue<bool>("alt-speed-enabled"); } set { this["alt-speed-enabled"] = value; } }
 
         /// <summary>
         /// When to turn on alt speeds (units: minutes after midnight)
         /// </summary>
-        public long? AlternativeSpeedTimeBegin { get { return GetValue<long?>("alt-speed-time-begin"); } set { this["alt-speed-time-begin"] = value; } }
+        public long AlternativeSpeedTimeBegin { get { return GetValue<long>("alt-speed-time-begin"); } set { this["alt-speed-time-begin"] = value; } }
 
         /// <summary>
         /// True means the scheduled on/off times are used
         /// </summary>
-        public bool? AlternativeSpeedTimeEnabled { get { return GetValue<bool?>("alt-speed-time-enabled"); } set { this["bandwidthPriority"] = value; } }
+        public bool AlternativeSpeedTimeEnabled { get { return GetValue<bool>("alt-speed-time-enabled"); } set { this["bandwidthPriority"] = value; } }
 
         /// <summary>
         /// When to turn off alt speeds
         /// </summary>
-        public long? AlternativeSpeedTimeEnd { get { return GetValue<long?>("alt-speed-time-end"); } set { this["alt-speed-time-end"] = value; } }
+        public long AlternativeSpeedTimeEnd { get { return GetValue<long>("alt-speed-time-end"); } set { this["alt-speed-time-end"] = value; } }
 
         /// <summary>
         /// What day(s) to turn on alt speeds
         /// </summary>
-        public long? AlternativeSpeedTimeDay { get { return GetValue<long?>("alt-speed-time-day"); } set { this["alt-speed-time-day"] = value; } }
+        public long AlternativeSpeedTimeDay { get { return GetValue<long>("alt-speed-time-day"); } set { this["alt-speed-time-day"] = value; } }
 
         /// <summary>
         /// Max global upload speed (KBps)
         /// </summary>
-        public long? AlternativeSpeedUp { get { return GetValue<long?>("alt-speed-up"); } set { this["alt-speed-up"] = value; } }
+        public long AlternativeSpeedUp { get { return GetValue<long>("alt-speed-up"); } set { this["alt-speed-up"] = value; } }
 
         /// <summary>
         /// Location of the blocklist to use for "blocklist-update"
@@ -57,12 +57,12 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// True means enabled
         /// </summary>
-        public bool? BlocklistEnabled { get { return GetValue<bool?>("blocklist-enabled"); } set { this["blocklist-enabled"] = value; } }
+        public bool BlocklistEnabled { get { return GetValue<bool>("blocklist-enabled"); } set { this["blocklist-enabled"] = value; } }
 
         /// <summary>
         /// Maximum size of the disk cache (MB)
         /// </summary>
-        public long? CacheSizeMb { get { return GetValue<long?>("cache-size-mb"); } set { this["cache-size-mb"] = value; } }
+        public long CacheSizeMb { get { return GetValue<long>("cache-size-mb"); } set { this["cache-size-mb"] = value; } }
 
         /// <summary>
         /// Announce URLs, one per line, and a blank line between tiers
@@ -77,17 +77,17 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Max number of torrents to download at once (see download-queue-enabled)
         /// </summary>
-        public long? DownloadQueueSize { get { return GetValue<long?>("download-queue-size"); } set { this["download-queue-size"] = value; } }
+        public long DownloadQueueSize { get { return GetValue<long>("download-queue-size"); } set { this["download-queue-size"] = value; } }
 
         /// <summary>
         /// If true, limit how many torrents can be downloaded at once
         /// </summary>
-        public bool? DownloadQueueEnabled { get { return GetValue<bool?>("download-queue-enabled"); } set { this["download-queue-enabled"] = value; } }
+        public bool DownloadQueueEnabled { get { return GetValue<bool>("download-queue-enabled"); } set { this["download-queue-enabled"] = value; } }
 
         /// <summary>
         /// True means allow dht in public torrents
         /// </summary>
-        public bool? DhtEnabled { get { return GetValue<bool?>("dht-enabled"); } set { this["dht-enabled"] = value; } }
+        public bool DhtEnabled { get { return GetValue<bool>("dht-enabled"); } set { this["dht-enabled"] = value; } }
 
         /// <summary>
         /// "required", "preferred", "tolerated"
@@ -95,14 +95,14 @@ public class SessionSettings : ArgumentsBase
         public string Encryption { get { return GetValue<string>("encryption"); } set { this["encryption"] = value; } }
 
         /// <summary>
-        /// Torrents we're seeding will be stopped if they're idle for this long?
+        /// Torrents we're seeding will be stopped if they're idle for this long
         /// </summary>
-        public long? IdleSeedingLimit { get { return GetValue<long?>("idle-seeding-limit"); } set { this["idle-seeding-limit"] = value; } }
+        public long IdleSeedingLimit { get { return GetValue<long>("idle-seeding-limit"); } set { this["idle-seeding-limit"] = value; } }
 
         /// <summary>
         /// True if the seeding inactivity limit is honored by default
         /// </summary>
-        public bool? IdleSeedingLimitEnabled { get { return GetValue<bool?>("idle-seeding-limit-enabled"); } set { this["idle-seeding-limit-enabled"] = value; } }
+        public bool IdleSeedingLimitEnabled { get { return GetValue<bool>("idle-seeding-limit-enabled"); } set { this["idle-seeding-limit-enabled"] = value; } }
 
         /// <summary>
         /// Path for incomplete torrents, when enabled
@@ -112,57 +112,57 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// True means keep torrents in incomplete-dir until done
         /// </summary>
-        public bool? IncompleteDirectoryEnabled { get { return GetValue<bool?>("incomplete-dir-enabled"); } set { this["incomplete-dir-enabled"] = value; } }
+        public bool IncompleteDirectoryEnabled { get { return GetValue<bool>("incomplete-dir-enabled"); } set { this["incomplete-dir-enabled"] = value; } }
 
         /// <summary>
         /// True means allow Local Peer Discovery in public torrents
         /// </summary>
-        public bool? LpdEnabled { get { return GetValue<bool?>("lpd-enabled"); } set { this["lpd-enabled"] = value; } }
+        public bool LpdEnabled { get { return GetValue<bool>("lpd-enabled"); } set { this["lpd-enabled"] = value; } }
 
         /// <summary>
         /// Maximum global number of peers
         /// </summary>
-        public long? PeerLimitGlobal { get { return GetValue<long?>("peer-limit-global"); } set { this["peer-limit-global"] = value; } }
+        public long PeerLimitGlobal { get { return GetValue<long>("peer-limit-global"); } set { this["peer-limit-global"] = value; } }
 
         /// <summary>
         /// Maximum global number of peers
         /// </summary>
-        public long? PeerLimitPerTorrent { get { return GetValue<long?>("peer-limit-per-torrent"); } set { this["peer-limit-per-torrent"] = value; } }
+        public long PeerLimitPerTorrent { get { return GetValue<long>("peer-limit-per-torrent"); } set { this["peer-limit-per-torrent"] = value; } }
 
         /// <summary>
         /// True means allow pex in public torrents
         /// </summary>
-        public bool? PexEnabled { get { return GetValue<bool?>("pex-enabled"); } set { this["pex-enabled"] = value; } }
+        public bool PexEnabled { get { return GetValue<bool>("pex-enabled"); } set { this["pex-enabled"] = value; } }
 
         /// <summary>
         /// Port number
         /// </summary>
-        public long? PeerPort { get { return GetValue<long?>("peer-port"); } set { this["peer-port"] = value; } }
+        public long PeerPort { get { return GetValue<long>("peer-port"); } set { this["peer-port"] = value; } }
 
         /// <summary>
         /// True means pick a random peer port on launch
         /// </summary>
-        public bool? PeerPortRandomOnStart { get { return GetValue<bool?>("peer-port-random-on-start"); } set { this["peer-port-random-on-start"] = value; } }
+        public bool PeerPortRandomOnStart { get { return GetValue<bool>("peer-port-random-on-start"); } set { this["peer-port-random-on-start"] = value; } }
 
         /// <summary>
         /// true means enabled
         /// </summary>
-        public bool? PortForwardingEnabled { get { return GetValue<bool?>("port-forwarding-enabled"); } set { this["port-forwarding-enabled"] = value; } }
+        public bool PortForwardingEnabled { get { return GetValue<bool>("port-forwarding-enabled"); } set { this["port-forwarding-enabled"] = value; } }
 
         /// <summary>
         /// Whether or not to consider idle torrents as stalled
         /// </summary>
-        public bool? QueueStalledEnabled { get { return GetValue<bool?>("queue-stalled-enabled"); } set { this["queue-stalled-enabled"] = value; } }
+        public bool QueueStalledEnabled { get { return GetValue<bool>("queue-stalled-enabled"); } set { this["queue-stalled-enabled"] = value; } }
 
         /// <summary>
         /// Torrents that are idle for N minuets aren't counted toward seed-queue-size or download-queue-size
         /// </summary>
-        public long? QueueStalledMinutes { get { return GetValue<long?>("queue-stalled-minutes"); } set { this["queue-stalled-minutes"] = value; } }
+        public long QueueStalledMinutes { get { return GetValue<long>("queue-stalled-minutes"); } set { this["queue-stalled-minutes"] = value; } }
 
         /// <summary>
         /// True means append ".part" to incomplete files
         /// </summary>
-        public bool? RenamePartialFiles { get { return GetValue<bool?>("rename-partial-files"); } set { this["rename-partial-files"] = value; } }
+        public bool RenamePartialFiles { get { return GetValue<bool>("rename-partial-files"); } set { this["rename-partial-files"] = value; } }
 
         /// <summary>
         /// Filename of the script to run
@@ -172,7 +172,7 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Whether or not to call the "added" script
         /// </summary>
-        public bool? ScriptTorrentAddedEnabled { get { return GetValue<bool?>("script-torrent-added-enabled"); } set { this["script-torrent-added-enabled"] = value; } }
+        public bool ScriptTorrentAddedEnabled { get { return GetValue<bool>("script-torrent-added-enabled"); } set { this["script-torrent-added-enabled"] = value; } }
 
         /// <summary>
         /// Filename of the script to run
@@ -182,7 +182,7 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Whether or not to call the "done" script
         /// </summary>
-        public bool? ScriptTorrentDoneEnabled { get { return GetValue<bool?>("script-torrent-done-enabled"); } set { this["script-torrent-done-enabled"] = value; } }
+        public bool ScriptTorrentDoneEnabled { get { return GetValue<bool>("script-torrent-done-enabled"); } set { this["script-torrent-done-enabled"] = value; } }
 
         /// <summary>
         /// Filename of the script to run
@@ -192,62 +192,62 @@ public class SessionSettings : ArgumentsBase
         /// <summary>
         /// Whether or not to call the "done seeding" script
         /// </summary>
-        public bool? ScriptTorrentDoneSeedingEnabled { get { return GetValue<bool?>("script-torrent-done-seeding-enabled"); } set { this["script-torrent-done-seeding-enabled"] = value; } }
+        public bool ScriptTorrentDoneSeedingEnabled { get { return GetValue<bool>("script-torrent-done-seeding-enabled"); } set { this["script-torrent-done-seeding-enabled"] = value; } }
 
         /// <summary>
         /// The default seed ratio for torrents to use
         /// </summary>
-        public double? SeedRatioLimit { get { return GetValue<long?>("seedRatioLimit"); } set { this["seedRatioLimit"] = value; } }
+        public double SeedRatioLimit { get { return GetValue<long>("seedRatioLimit"); } set { this["seedRatioLimit"] = value; } }
 
         /// <summary>
         /// True if seedRatioLimit is honored by default
         /// </summary>
-        public bool? SeedRatioLimited { get { return GetValue<bool?>("seedRatioLimited"); } set { this["seedRatioLimited"] = value; } }
+        public bool SeedRatioLimited { get { return GetValue<bool>("seedRatioLimited"); } set { this["seedRatioLimited"] = value; } }
 
         /// <summary>
         /// Max number of torrents to uploaded at once (see seed-queue-enabled)
         /// </summary>
-        public long? SeedQueueSize { get { return GetValue<long?>("seed-queue-size"); } set { this["seed-queue-size"] = value; } }
+        public long SeedQueueSize { get { return GetValue<long>("seed-queue-size"); } set { this["seed-queue-size"] = value; } }
 
         /// <summary>
         /// If true, limit how many torrents can be uploaded at once
         /// </summary>
-        public bool? SeedQueueEnabled { get { return GetValue<bool?>("seed-queue-enabled"); } set { this["seed-queue-enabled"] = value; } }
+        public bool SeedQueueEnabled { get { return GetValue<bool>("seed-queue-enabled"); } set { this["seed-queue-enabled"] = value; } }
 
         /// <summary>
         /// Max global download speed (KBps)
         /// </summary>
-        public long? SpeedLimitDown { get { return GetValue<long?>("speed-limit-down"); } set { this["speed-limit-down"] = value; } }
+        public long SpeedLimitDown { get { return GetValue<long>("speed-limit-down"); } set { this["speed-limit-down"] = value; } }
 
         /// <summary>
         /// True means enabled
         /// </summary>
-        public bool? SpeedLimitDownEnabled { get { return GetValue<bool?>("speed-limit-down-enabled"); } set { this["speed-limit-down-enabled"] = value; } }
+        public bool SpeedLimitDownEnabled { get { return GetValue<bool>("speed-limit-down-enabled"); } set { this["speed-limit-down-enabled"] = value; } }
 
         /// <summary>
         ///  max global upload speed (KBps)
         /// </summary>
-        public long? SpeedLimitUp { get { return GetValue<long?>("speed-limit-up"); } set { this["speed-limit-up"] = value; } }
+        public long SpeedLimitUp { get { return GetValue<long>("speed-limit-up"); } set { this["speed-limit-up"] = value; } }
 
         /// <summary>
         /// True means enabled
         /// </summary>
-        public bool? SpeedLimitUpEnabled { get { return GetValue<bool?>("speed-limit-up-enabled"); } set { this["speed-limit-up-enabled"] = value; } }
+        public bool SpeedLimitUpEnabled { get { return GetValue<bool>("speed-limit-up-enabled"); } set { this["speed-limit-up-enabled"] = value; } }
 
         /// <summary>
         /// True means added torrents will be started right away
         /// </summary>
-        public bool? StartAddedTorrents { get { return GetValue<bool?>("start-added-torrents"); } set { this["start-added-torrents"] = value; } }
+        public bool StartAddedTorrents { get { return GetValue<bool>("start-added-torrents"); } set { this["start-added-torrents"] = value; } }
 
         /// <summary>
         /// True means the .torrent file of added torrents will be deleted
         /// </summary>
-        public bool? TrashOriginalTorrentFiles { get { return GetValue<bool?>("trash-original-torrent-files"); } set { this["trash-original-torrent-files"] = value; } }
+        public bool TrashOriginalTorrentFiles { get { return GetValue<bool>("trash-original-torrent-files"); } set { this["trash-original-torrent-files"] = value; } }
 
         /// <summary>
         /// True means allow utp
         /// </summary>
-        public bool? UtpEnabled { get { return GetValue<bool?>("utp-enabled"); } set { this["utp-enabled"] = value; } }
+        public bool UtpEnabled { get { return GetValue<bool>("utp-enabled"); } set { this["utp-enabled"] = value; } }
 
     }
 }
diff --git a/Transmission.API.RPC/Arguments/TorrentFields.cs b/Transmission.API.RPC/Arguments/TorrentFields.cs
index a04c856..cbc2be9 100644
--- a/Transmission.API.RPC/Arguments/TorrentFields.cs
+++ b/Transmission.API.RPC/Arguments/TorrentFields.cs
@@ -405,7 +405,7 @@ public static string[] ALL_FIELDS
         {
             get
             {
-                return new string[] 
+                return new string[]
                 {
                     #region ALL FIELDS
                     ACTIVITY_DATE,
diff --git a/Transmission.API.RPC/Arguments/TorrentSettings.cs b/Transmission.API.RPC/Arguments/TorrentSettings.cs
index b0e1276..3a2ec1f 100644
--- a/Transmission.API.RPC/Arguments/TorrentSettings.cs
+++ b/Transmission.API.RPC/Arguments/TorrentSettings.cs
@@ -17,17 +17,17 @@ public class TorrentSettings : ArgumentsBase
         /// <summary>
         /// This torrent's bandwidth tr_priority_t
         /// </summary>
-        public long? BandwidthPriority { get { return GetValue<long?>("bandwidthPriority"); } set { this["bandwidthPriority"] = value; } }
+        public long BandwidthPriority { get { return GetValue<long>("bandwidthPriority"); } set { this["bandwidthPriority"] = value; } }
 
         /// <summary>
         /// Maximum download speed (KBps)
         /// </summary>
-        public long? DownloadLimit { get { return GetValue<long?>("downloadLimit"); } set { this["downloadLimit"] = value; } }
+        public long DownloadLimit { get { return GetValue<long>("downloadLimit"); } set { this["downloadLimit"] = value; } }
 
         /// <summary>
         /// Download limit is honored
         /// </summary>
-        public bool? DownloadLimited { get { return GetValue<bool?>("downloadLimited"); } set { this["downloadLimited"] = value; } }
+        public bool DownloadLimited { get { return GetValue<bool>("downloadLimited"); } set { this["downloadLimited"] = value; } }
 
         /// <summary>
         /// The name of this torrent's bandwidth group
@@ -37,7 +37,7 @@ public class TorrentSettings : ArgumentsBase
         /// <summary>
         /// Session upload limits are honored
         /// </summary>
-        public bool? HonorsSessionLimits { get { return GetValue<bool?>("honorsSessionLimits"); } set { this["honorsSessionLimits"] = value; } }
+        public bool HonorsSessionLimits { get { return GetValue<bool>("honorsSessionLimits"); } set { this["honorsSessionLimits"] = value; } }
 
         /// <summary>
         /// Torrent id array
@@ -57,50 +57,50 @@ public class TorrentSettings : ArgumentsBase
         /// <summary>
         /// Maximum number of peers
         /// </summary>
-        public long? PeerLimit { get { return GetValue<long?>("peer-limit"); } set { this["peer-limit"] = value; } }
+        public long PeerLimit { get { return GetValue<long>("peer-limit"); } set { this["peer-limit"] = value; } }
 
         /// <summary>
         /// Position of this torrent in its queue [0...n)
         /// </summary>
-        public long? QueuePosition { get { return GetValue<long?>("queuePosition"); } set { this["queuePosition"] = value; } }
+        public long QueuePosition { get { return GetValue<long>("queuePosition"); } set { this["queuePosition"] = value; } }
 
         /// <summary>
         /// Torrent-level number of minutes of seeding inactivity
         /// </summary>
-        public long? SeedIdleLimit { get { return GetValue<long?>("seedIdleLimit"); } set { this["seedIdleLimit"] = value; } }
+        public long SeedIdleLimit { get { return GetValue<long>("seedIdleLimit"); } set { this["seedIdleLimit"] = value; } }
 
         /// <summary>
         /// Which seeding inactivity mode to use. 0=Global 1=Single 2=Unlimited
         /// </summary>
-        public long? SeedIdleMode { get { return GetValue<long?>("seedIdleMode"); } set { this["seedIdleMode"] = value; } }
+        public long SeedIdleMode { get { return GetValue<long>("seedIdleMode"); } set { this["seedIdleMode"] = value; } }
 
         /// <summary>
         /// Torrent-level seeding ratio
         /// </summary>
-        public double? SeedRatioLimit { get { return GetValue<double?>("seedRatioLimit"); } set { this["seedRatioLimit"] = value; } }
+        public double SeedRatioLimit { get { return GetValue<double>("seedRatioLimit"); } set { this["seedRatioLimit"] = value; } }
 
         /// <summary>
         /// Which ratio mode to use. 0=Global 1=Single 2=Unlimited
         /// </summary>
-        public long? SeedRatioMode { get { return GetValue<long?>("seedRatioMode"); } set { this["seedRatioMode"] = value; } }
+        public long SeedRatioMode { get { return GetValue<long>("seedRatioMode"); } set { this["seedRatioMode"] = value; } }
 
         /// <summary>
         /// Whether to download the torrent sequentially
         /// </summary>
-        public bool? SequentialDownload { get { return GetValue<bool?>("sequentialDownload"); } set { this["sequentialDownload"] = value; } }
+        public bool SequentialDownload { get { return GetValue<bool>("sequentialDownload"); } set { this["sequentialDownload"] = value; } }
 
         /// <summary>
         /// Maximum upload speed (KBps)
         /// </summary>
-        public long? UploadLimit { get { return GetValue<long?>("uploadLimit"); } set { this["uploadLimit"] = value; } }
+        public long UploadLimit { get { return GetValue<long>("uploadLimit"); } set { this["uploadLimit"] = value; } }
 
         /// <summary>
         /// Upload limit is honored
         /// </summary>
-        public bool? UploadLimited { get { return GetValue<bool?>("uploadLimited"); } set { this["uploadLimited"] = value; } }
+        public bool UploadLimited { get { return GetValue<bool>("uploadLimited"); } set { this["uploadLimited"] = value; } }
 
         /// <summary>
-        /// Strings of announce URLs to add
+        /// strings of announce URLs to add
         /// </summary>
         [Obsolete("TrackerAdd is obsolete since Transmission 4.0.0, use TrackerList instead.")]
 		public string[] TrackerAdd { get { return GetValue<string[]>("trackerAdd"); } set { this["trackerAdd"] = value; } }
@@ -109,16 +109,16 @@ public class TorrentSettings : ArgumentsBase
         /// Ids of trackers to remove
         /// </summary>
         [Obsolete("TrackerRemove is obsolete since Transmission 4.0.0, use TrackerList instead.")]
-		public long?[] TrackerRemove { get { return GetValue<long?[]>("trackerRemove"); } set { this["trackerRemove"] = value; } }
+		public long[] TrackerRemove { get { return GetValue<long[]>("trackerRemove"); } set { this["trackerRemove"] = value; } }
 
         /// <summary>
         /// Pairs of IDs of announce URLs to replace along with their new value
         /// </summary>
         [Obsolete("TrackerReplace is obsolete since Transmission 4.0.0, use TrackerList instead.")]
-        public KeyValuePair<int, string>?[] TrackerReplace { get { return GetValue<KeyValuePair<int, string>?[]>("trackerReplace"); } set { this["trackerReplace"] = value; } }
+        public KeyValuePair<int, string>[] TrackerReplace { get { return GetValue<KeyValuePair<int, string>[]>("trackerReplace"); } set { this["trackerReplace"] = value; } }
 
         /// <summary>
-        /// String of announce URLs, one per line, with a blank line between tiers
+        /// string of announce URLs, one per line, with a blank line between tiers
         /// </summary>
         public string[] TrackerList { get { return GetValue<string[]>("trackerList"); } set { this["trackerList"] = value; } }
 
diff --git a/Transmission.API.RPC/Client.Async.cs b/Transmission.API.RPC/Client.Async.cs
index fbcbd3c..318c5b4 100644
--- a/Transmission.API.RPC/Client.Async.cs
+++ b/Transmission.API.RPC/Client.Async.cs
@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
@@ -42,11 +43,11 @@ public async Task SetSessionSettingsAsync(SessionSettings settings)
         /// Get session stat
         /// </summary>
         /// <returns>Session stat</returns>
-        public async Task<Statistic> GetSessionStatisticAsync()
+        public async Task<Statistic?> GetSessionStatisticAsync()
         {
             var request = new TransmissionRequest("session-stats");
             var response = await SendRequestAsync(request);
-            var result = response.Deserialize<Statistic>();
+            var result = response?.Arguments.ToObject<Statistic>();
             return result;
         }
 
@@ -54,11 +55,11 @@ public async Task<Statistic> GetSessionStatisticAsync()
         /// Get information of current session (API: session-get)
         /// </summary>
         /// <returns>Session information</returns>
-        public async Task<SessionInfo> GetSessionInformationAsync()
+        public async Task<SessionInfo?> GetSessionInformationAsync()
         {
             var request = new TransmissionRequest("session-get");
             var response = await SendRequestAsync(request);
-            var result = response.Deserialize<SessionInfo>();
+            var result = response?.Arguments.ToObject<SessionInfo>();
             return result;
         }
 
@@ -69,14 +70,14 @@ public async Task<SessionInfo> GetSessionInformationAsync()
         /// </summary>
         /// <param name="fields">Optional fields of session information</param>
         /// <returns>Session information</returns>
-        public async Task<SessionInfo> GetSessionInformationAsync(string[] fields)
+        public async Task<SessionInfo?> GetSessionInformationAsync(string[] fields)
         {
             var arguments = new Dictionary<string, object>();
             arguments.Add("fields", fields);
 
             var request = new TransmissionRequest("session-get", arguments);
             var response = await SendRequestAsync(request);
-            var result = response.Deserialize<SessionInfo>();
+            var result = response?.Arguments.ToObject<SessionInfo>();
             return result;
         }
         #endregion
@@ -87,30 +88,30 @@ public async Task<SessionInfo> GetSessionInformationAsync(string[] fields)
         /// Add torrent (API: torrent-add)
         /// </summary>
         /// <returns>Torrent info (ID, Name and HashString)</returns>
-        public async Task<NewTorrentInfo> TorrentAddAsync(NewTorrent torrent)
+        public async Task<NewTorrentInfo?> TorrentAddAsync(NewTorrent torrent)
         {
             if (String.IsNullOrWhiteSpace(torrent.Metainfo) && String.IsNullOrWhiteSpace(torrent.Filename))
-                throw new Exception("Either \"filename\" or \"metainfo\" must be included.");
+                throw new ArgumentException("Either \"filename\" or \"metainfo\" must be included.");
 
             var request = new TransmissionRequest("torrent-add", torrent);
             var response = await SendRequestAsync(request);
-            var jObject = response.Deserialize<JObject>();
+            var jObject = response?.Arguments;
 
             if (jObject == null || jObject.First == null)
                 return null;
 
-            NewTorrentInfo result = null;
-            JToken value = null;
+            NewTorrentInfo? result = null;
+            JToken? value = null;
 
             if (jObject.TryGetValue("torrent-duplicate", out value))
             {
                 result = JsonConvert.DeserializeObject<NewTorrentInfo>(value.ToString());
-                result.Duplicate = true;
+                if (result != null) result.Duplicate = true;
             }
             else if (jObject.TryGetValue("torrent-added", out value))
             {
                 result = JsonConvert.DeserializeObject<NewTorrentInfo>(value.ToString());
-                result.Duplicate = false;
+                if (result != null) result.Duplicate = false;
             }
 
             return result;
@@ -130,41 +131,57 @@ public async Task TorrentSetAsync(TorrentSettings settings)
         /// Get fields of recently active torrents (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
+        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <returns>Torrents info</returns>
-        public async Task<TransmissionTorrents> TorrentGetRecentlyActiveAsync(string[] fields)
+        public async Task<TransmissionTorrents?> TorrentGetRecentlyActiveAsync(string[] fields, bool asObjects = false)
         {
             var arguments = new Dictionary<string, object>();
             arguments.Add("fields", fields);
             arguments.Add("ids", "recently-active");
 
-            var request = new TransmissionRequest("torrent-get", arguments);
+            if (asObjects) arguments.Add("format", "objects");
+            else arguments.Add("format", "table");
 
+            var request = new TransmissionRequest("torrent-get", arguments);
             var response = await SendRequestAsync(request);
-            var result = response.Deserialize<TransmissionTorrents>();
 
-            return result;
+            if (response == null) return null;
+
+            TransmissionTorrents? torrents;
+            if (asObjects) torrents = response.Arguments.ToObject<TransmissionTorrents>();
+            else torrents = TorrentInfoConverter.ConvertFromJObject(response.Arguments);
+
+            return torrents;
         }
 
         /// <summary>
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
+        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
         /// <returns>Torrents info</returns>
-        public async Task<TransmissionTorrents> TorrentGetAsync(string[] fields, params int[] ids)
+        public async Task<TransmissionTorrents?> TorrentGetAsync(string[] fields, bool asObjects = false, params long[] ids)
         {
             var arguments = new Dictionary<string, object>();
             arguments.Add("fields", fields);
 
+            if (asObjects) arguments.Add("format", "objects");
+            else arguments.Add("format", "table");
+
             if (ids != null && ids.Length > 0)
                 arguments.Add("ids", ids);
 
             var request = new TransmissionRequest("torrent-get", arguments);
-
             var response = await SendRequestAsync(request);
-            var result = response.Deserialize<TransmissionTorrents>();
 
-            return result;
+            if (response == null) return null;
+
+            TransmissionTorrents? torrents;
+            if (asObjects) torrents = response?.Arguments.ToObject<TransmissionTorrents>();
+            else torrents = TorrentInfoConverter.ConvertFromJObject(response.Arguments);
+
+            return torrents;
         }
 
         /// <summary>
@@ -172,7 +189,7 @@ public async Task<TransmissionTorrents> TorrentGetAsync(string[] fields, params
         /// </summary>
         /// <param name="ids">Torrents id</param>
         /// <param name="deleteData">Remove data</param>
-        public async Task TorrentRemoveAsync(int[] ids, bool deleteData = false)
+        public async Task TorrentRemoveAsync(long[] ids, bool deleteData = false)
         {
             var arguments = new Dictionary<string, object>();
 
@@ -304,7 +321,7 @@ public async Task TorrentReannounceRecentlyActiveAsync()
         /// Move torrents in queue on top (API: queue-move-top)
         /// </summary>
         /// <param name="ids">Torrents id</param>
-        public async Task TorrentQueueMoveTopAsync(int[] ids)
+        public async Task TorrentQueueMoveTopAsync(long[] ids)
         {
             var request = new TransmissionRequest("queue-move-top", new Dictionary<string, object> { { "ids", ids } });
             var response = await SendRequestAsync(request);
@@ -314,7 +331,7 @@ public async Task TorrentQueueMoveTopAsync(int[] ids)
         /// Move up torrents in queue (API: queue-move-up)
         /// </summary>
         /// <param name="ids"></param>
-        public async Task TorrentQueueMoveUpAsync(int[] ids)
+        public async Task TorrentQueueMoveUpAsync(long[] ids)
         {
             var request = new TransmissionRequest("queue-move-up", new Dictionary<string, object> { { "ids", ids } });
             var response = await SendRequestAsync(request);
@@ -324,7 +341,7 @@ public async Task TorrentQueueMoveUpAsync(int[] ids)
         /// Move down torrents in queue (API: queue-move-down)
         /// </summary>
         /// <param name="ids"></param>
-        public async Task TorrentQueueMoveDownAsync(int[] ids)
+        public async Task TorrentQueueMoveDownAsync(long[] ids)
         {
             var request = new TransmissionRequest("queue-move-down", new Dictionary<string, object> { { "ids", ids } });
             var response = await SendRequestAsync(request);
@@ -334,7 +351,7 @@ public async Task TorrentQueueMoveDownAsync(int[] ids)
         /// Move torrents to bottom in queue  (API: queue-move-bottom)
         /// </summary>
         /// <param name="ids"></param>
-        public async Task TorrentQueueMoveBottomAsync(int[] ids)
+        public async Task TorrentQueueMoveBottomAsync(long[] ids)
         {
             var request = new TransmissionRequest("queue-move-bottom", new Dictionary<string, object> { { "ids", ids } });
             var response = await SendRequestAsync(request);
@@ -348,7 +365,7 @@ public async Task TorrentQueueMoveBottomAsync(int[] ids)
         /// <param name="ids">Torrent ids</param>
         /// <param name="location">The new torrent location</param>
         /// <param name="move">Move from previous location</param>
-        public async Task TorrentSetLocationAsync(int[] ids, string location, bool move)
+        public async Task TorrentSetLocationAsync(long[] ids, string location, bool move)
         {
             var arguments = new Dictionary<string, object>();
             arguments.Add("ids", ids);
@@ -365,17 +382,17 @@ public async Task TorrentSetLocationAsync(int[] ids, string location, bool move)
         /// <param name="id">The torrent whose path will be renamed</param>
         /// <param name="path">The path to the file or folder that will be renamed</param>
         /// <param name="name">The file or folder's new name</param>
-        public async Task<RenameTorrentInfo> TorrentRenamePathAsync(int id, string path, string name)
+        public async Task<RenameTorrentInfo?> TorrentRenamePathAsync(long id, string path, string name)
         {
             var arguments = new Dictionary<string, object>();
-            arguments.Add("ids", new int[] { id });
+            arguments.Add("ids", new long[] { id });
             arguments.Add("path", path);
             arguments.Add("name", name);
 
             var request = new TransmissionRequest("torrent-rename-path", arguments);
             var response = await SendRequestAsync(request);
 
-            var result = response.Deserialize<RenameTorrentInfo>();
+            var result = response?.Arguments.ToObject<RenameTorrentInfo>();
 
             return result;
         }
@@ -388,12 +405,12 @@ public async Task<RenameTorrentInfo> TorrentRenamePathAsync(int id, string path,
         /// Get bandwidth groups (API: group-get)
         /// </summary>
         /// <returns></returns>
-        public async Task<BandwidthGroup[]> BandwidthGroupGetAsync()
+        public async Task<BandwidthGroup[]?> BandwidthGroupGetAsync()
         {
             var request = new TransmissionRequest("group-get");
 
             var response = await SendRequestAsync(request);
-            var result = response.Deserialize<BandwidthGroup[]>();
+            var result = response?.Arguments.ToObject<BandwidthGroup[]>();
 
             return result;
         }
@@ -403,7 +420,7 @@ public async Task<BandwidthGroup[]> BandwidthGroupGetAsync()
         /// </summary>
         /// <param name="groups">Optional names of groups to get</param>
         /// <returns></returns>
-        public async Task<BandwidthGroup[]> BandwidthGroupGetAsync(string[] groups)
+        public async Task<BandwidthGroup[]?> BandwidthGroupGetAsync(string[] groups)
         {
             var arguments = new Dictionary<string, object>();
             arguments.Add("group", groups);
@@ -411,7 +428,7 @@ public async Task<BandwidthGroup[]> BandwidthGroupGetAsync(string[] groups)
             var request = new TransmissionRequest("group-get", arguments);
 
             var response = await SendRequestAsync(request);
-            var result = response.Deserialize<BandwidthGroup[]>();
+            var result = response?.Arguments.ToObject<BandwidthGroup[]>();
 
             return result;
         }
@@ -434,36 +451,39 @@ public async Task BandwidthGroupSetAsync(BandwidthGroupSettings group)
         /// See if your incoming peer port is accessible from the outside world (API: port-test)
         /// </summary>
         /// <returns>A Tuple with a boolean of whether the port test succeeded, and a PortTestProtocol enum of which protocol was used for the test</returns>
-        public async Task<Tuple<bool, PortTestProtocol>> PortTestAsync()
+        public async Task<Tuple<bool?, PortTestProtocol>> PortTestAsync()
         {
             var request = new TransmissionRequest("port-test");
             var response = await SendRequestAsync(request);
 
-            var data = response.Deserialize<JObject>();
-            var result = (bool)data.GetValue("port-is-open");
+            var data = response?.Arguments;
+            var result = (bool?)data?.GetValue("port-is-open");
             PortTestProtocol protocol = PortTestProtocol.Unknown;
-            if (data.TryGetValue("ipProtocol", out var protocolValue))
+            if (data?.TryGetValue("ipProtocol", out var protocolValue) == true)
             {
-                switch ((string)protocolValue)
+                if (protocolValue != null)
                 {
-                    case "ipv4": protocol = PortTestProtocol.IPv4; break;
-                    case "ipv6": protocol = PortTestProtocol.IPV6; break;
+                    switch ((string?)protocolValue)
+                    {
+                        case "ipv4": protocol = PortTestProtocol.IPv4; break;
+                        case "ipv6": protocol = PortTestProtocol.IPV6; break;
+                    }
                 }
             }
-            return new Tuple<bool, PortTestProtocol>(result, protocol);
+            return new Tuple<bool?, PortTestProtocol>(result, protocol);
         }
 
         /// <summary>
         /// Update blocklist (API: blocklist-update)
         /// </summary>
         /// <returns>Blocklist size</returns>
-        public async Task<int> BlocklistUpdateAsync()
+        public async Task<long?> BlocklistUpdateAsync()
         {
             var request = new TransmissionRequest("blocklist-update");
             var response = await SendRequestAsync(request);
 
-            var data = response.Deserialize<JObject>();
-            var result = (int)data.GetValue("blocklist-size");
+            var data = response?.Arguments;
+            var result = (long?)data?.GetValue("blocklist-size");
             return result;
         }
 
@@ -471,7 +491,7 @@ public async Task<int> BlocklistUpdateAsync()
         /// Get free space is available in a client-specified folder.
         /// </summary>
         /// <param name="path">The directory to query</param>
-        public async Task<FreeSpace> FreeSpaceAsync(string path)
+        public async Task<FreeSpace?> FreeSpaceAsync(string path)
         {
             var arguments = new Dictionary<string, object>();
             arguments.Add("path", path);
@@ -479,15 +499,15 @@ public async Task<FreeSpace> FreeSpaceAsync(string path)
             var request = new TransmissionRequest("free-space", arguments);
             var response = await SendRequestAsync(request);
 
-            var data = response.Deserialize<FreeSpace>();
+            var data = response?.Arguments.ToObject<FreeSpace>();
             return data;
         }
 
         #endregion
 
-        private async Task<TransmissionResponse> SendRequestAsync(TransmissionRequest request)
+        private async Task<TransmissionResponse?> SendRequestAsync(TransmissionRequest request)
         {
-            TransmissionResponse result = new TransmissionResponse();
+            TransmissionResponse? result = null;
 
             request.Tag = ++CurrentTag;
 
@@ -508,10 +528,10 @@ private async Task<TransmissionResponse> SendRequestAsync(TransmissionRequest re
                 if (httpResponse.IsSuccessStatusCode)
                 {
                     var responseString = await httpResponse.Content.ReadAsStringAsync();
-                    result = JsonConvert.DeserializeObject<TransmissionResponse>(responseString);
+                    result = new TransmissionResponse(responseString);
 
                     if (result.Result != "success")
-                        throw new Exception(result.Result);
+                        throw new RequestFailedException(result.Result);
                 }
                 else if (httpResponse.StatusCode == HttpStatusCode.Conflict)
                 {
@@ -521,7 +541,7 @@ private async Task<TransmissionResponse> SendRequestAsync(TransmissionRequest re
                         if (httpResponse.Headers.TryGetValues("X-Transmission-Session-Id", out var values))
                             SessionID = values.First();
                         else
-                            throw new Exception("Session ID Error");
+                            throw new SessionIdException();
 
                         result = await SendRequestAsync(request);
                     }
diff --git a/Transmission.API.RPC/Client.cs b/Transmission.API.RPC/Client.cs
index abb142d..3a765e3 100644
--- a/Transmission.API.RPC/Client.cs
+++ b/Transmission.API.RPC/Client.cs
@@ -36,7 +36,7 @@ public string SessionID
         /// <summary>
         /// Current Tag
         /// </summary>
-        public int CurrentTag
+        public long CurrentTag
         {
             get;
             private set;
@@ -150,10 +150,11 @@ public void TorrentSet(TorrentSettings settings)
         /// Get fields of recently active torrents (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
+        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <returns>Torrents info</returns>
-        public TransmissionTorrents TorrentGetRecentlyActive(string[] fields)
+        public TransmissionTorrents TorrentGetRecentlyActive(string[] fields, bool asObjects = false)
         {
-            var task = TorrentGetRecentlyActiveAsync(fields);
+            var task = TorrentGetRecentlyActiveAsync(fields, asObjects);
             task.WaitAndUnwrapException();
             return task.Result;
         }
@@ -162,11 +163,12 @@ public TransmissionTorrents TorrentGetRecentlyActive(string[] fields)
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
+        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
         /// <returns>Torrents info</returns>
-        public TransmissionTorrents TorrentGet(string[] fields, params int[] ids)
+        public TransmissionTorrents TorrentGet(string[] fields, bool asObjects = false, params long[] ids)
         {
-            var task = TorrentGetAsync(fields, ids);
+            var task = TorrentGetAsync(fields, asObjects, ids);
             task.WaitAndUnwrapException();
             return task.Result;
         }
@@ -176,7 +178,7 @@ public TransmissionTorrents TorrentGet(string[] fields, params int[] ids)
         /// </summary>
         /// <param name="ids">Torrents id</param>
         /// <param name="deleteData">Remove data</param>
-        public void TorrentRemove(int[] ids, bool deleteData = false)
+        public void TorrentRemove(long[] ids, bool deleteData = false)
         {
             TorrentRemoveAsync(ids, deleteData).WaitAndUnwrapException();
         }
@@ -287,7 +289,7 @@ public void TorrentReannounceRecentlyActive()
         /// Move torrents in queue on top (API: queue-move-top)
         /// </summary>
         /// <param name="ids">Torrents id</param>
-        public void TorrentQueueMoveTop(int[] ids)
+        public void TorrentQueueMoveTop(long[] ids)
         {
             TorrentQueueMoveTopAsync(ids).WaitAndUnwrapException();
         }
@@ -296,7 +298,7 @@ public void TorrentQueueMoveTop(int[] ids)
         /// Move up torrents in queue (API: queue-move-up)
         /// </summary>
         /// <param name="ids"></param>
-        public void TorrentQueueMoveUp(int[] ids)
+        public void TorrentQueueMoveUp(long[] ids)
         {
             TorrentQueueMoveUpAsync(ids).WaitAndUnwrapException();
         }
@@ -305,7 +307,7 @@ public void TorrentQueueMoveUp(int[] ids)
         /// Move down torrents in queue (API: queue-move-down)
         /// </summary>
         /// <param name="ids"></param>
-        public void TorrentQueueMoveDown(int[] ids)
+        public void TorrentQueueMoveDown(long[] ids)
         {
             TorrentQueueMoveDownAsync(ids).WaitAndUnwrapException();
         }
@@ -314,7 +316,7 @@ public void TorrentQueueMoveDown(int[] ids)
         /// Move torrents to bottom in queue  (API: queue-move-bottom)
         /// </summary>
         /// <param name="ids"></param>
-        public void TorrentQueueMoveBottom(int[] ids)
+        public void TorrentQueueMoveBottom(long[] ids)
         {
             TorrentQueueMoveBottomAsync(ids).WaitAndUnwrapException();
         }
@@ -327,7 +329,7 @@ public void TorrentQueueMoveBottom(int[] ids)
         /// <param name="ids">Torrent ids</param>
         /// <param name="location">The new torrent location</param>
         /// <param name="move">Move from previous location</param>
-        public void TorrentSetLocation(int[] ids, string location, bool move)
+        public void TorrentSetLocation(long[] ids, string location, bool move)
         {
             TorrentSetLocationAsync(ids, location, move).WaitAndUnwrapException();
         }
@@ -338,7 +340,7 @@ public void TorrentSetLocation(int[] ids, string location, bool move)
         /// <param name="id">The torrent whose path will be renamed</param>
         /// <param name="path">The path to the file or folder that will be renamed</param>
         /// <param name="name">The file or folder's new name</param>
-		public RenameTorrentInfo TorrentRenamePath(int id, string path, string name)
+		public RenameTorrentInfo TorrentRenamePath(long id, string path, string name)
         {
             var task = TorrentRenamePathAsync(id, path, name);
             task.WaitAndUnwrapException();
@@ -388,7 +390,7 @@ public void BandwidthGroupSet(BandwidthGroupSettings group)
         /// See if your incoming peer port is accessible from the outside world (API: port-test)
         /// </summary>
         /// <returns>Accessible state</returns>
-        public Tuple<bool, PortTestProtocol> PortTest()
+        public Tuple<bool?, PortTestProtocol> PortTest()
         {
             var task = PortTestAsync();
             task.WaitAndUnwrapException();
@@ -399,7 +401,7 @@ public Tuple<bool, PortTestProtocol> PortTest()
         /// Update blocklist (API: blocklist-update)
         /// </summary>
         /// <returns>Blocklist size</returns>
-        public int BlocklistUpdate()
+        public long? BlocklistUpdate()
         {
             var task = BlocklistUpdateAsync();
             task.WaitAndUnwrapException();
diff --git a/Transmission.API.RPC/Common/CommunicateBase.cs b/Transmission.API.RPC/Common/CommunicateBase.cs
deleted file mode 100644
index ed999fa..0000000
--- a/Transmission.API.RPC/Common/CommunicateBase.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using Newtonsoft.Json;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Transmission.API.RPC.Common
-{
-    /// <summary>
-    /// Base class for request/response
-    /// </summary>
-    public abstract class CommunicateBase
-    {
-        /// <summary>
-        /// Data
-        /// </summary>
-        [JsonProperty("arguments")]
-        public Dictionary<string, object> Arguments;
-
-        /// <summary>
-        /// Number (id)
-        /// </summary>
-        [JsonProperty("tag")]
-        public int Tag;
-
-        /// <summary>
-        /// Convert to JSON string
-        /// </summary>
-        /// <returns></returns>
-        public virtual string ToJson()
-        {
-            return JsonConvert.SerializeObject(this, Formatting.Indented);
-        }
-
-        /// <summary>
-        /// Deserialize to class
-        /// </summary>
-        /// <returns></returns>
-        public T Deserialize<T>()
-        {
-            var argumentsString = JsonConvert.SerializeObject(this.Arguments);
-            return JsonConvert.DeserializeObject<T>(argumentsString);
-        }
-    }
-}
diff --git a/Transmission.API.RPC/Common/TransmissionRequest.cs b/Transmission.API.RPC/Common/TransmissionRequest.cs
index 6396a8a..597a52c 100644
--- a/Transmission.API.RPC/Common/TransmissionRequest.cs
+++ b/Transmission.API.RPC/Common/TransmissionRequest.cs
@@ -10,7 +10,7 @@ namespace Transmission.API.RPC.Common
 	/// <summary>
 	/// Transmission request 
 	/// </summary>
-	public class TransmissionRequest : CommunicateBase
+	public class TransmissionRequest
 	{
 		/// <summary>
 		/// Name of the method to invoke
@@ -18,6 +18,18 @@ public class TransmissionRequest : CommunicateBase
 		[JsonProperty("method")]
 		public string Method;
 
+        /// <summary>
+        /// Data
+        /// </summary>
+        [JsonProperty("arguments")]
+        public Dictionary<string, object> Arguments;
+
+        /// <summary>
+        /// Number (id)
+        /// </summary>
+        [JsonProperty("tag")]
+        public long Tag;
+
         /// <summary>
         /// Initialize request
         /// </summary>
@@ -48,5 +60,14 @@ public TransmissionRequest(string method, Dictionary<string, object> arguments)
             this.Method = method;
             this.Arguments = arguments;
         }
-	}
+
+        /// <summary>
+        /// Convert to JSON string
+        /// </summary>
+        /// <returns></returns>
+        public virtual string ToJson()
+        {
+            return JsonConvert.SerializeObject(this, Formatting.Indented);
+        }
+    }
 }
diff --git a/Transmission.API.RPC/Common/TransmissionResponse.cs b/Transmission.API.RPC/Common/TransmissionResponse.cs
index 9fde714..d3c29b7 100644
--- a/Transmission.API.RPC/Common/TransmissionResponse.cs
+++ b/Transmission.API.RPC/Common/TransmissionResponse.cs
@@ -1,6 +1,8 @@
 using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -10,12 +12,51 @@ namespace Transmission.API.RPC.Common
 	/// <summary>
 	/// Transmission response 
 	/// </summary>
-	public class TransmissionResponse : CommunicateBase
+	public class TransmissionResponse
 	{
 		/// <summary>
 		/// Contains "success" on success, or an error string on failure.
 		/// </summary>
-		[JsonProperty("result")]
-		public string Result;
-	}
+        public string Result { get; }
+
+        /// <summary>
+        /// Uniquely identifies which request this is a response to
+        /// </summary>
+        public int? Tag;
+
+        /// <summary>
+        /// Data
+        /// </summary>
+        public JObject Arguments { get; }
+
+        public TransmissionResponse(string json)
+        {
+            using var stringReader = new StringReader(json);
+            using var jsonReader = new JsonTextReader(stringReader);
+
+            while (jsonReader.Read())
+            {
+                if (jsonReader.TokenType == JsonToken.PropertyName)
+                {
+                    if (jsonReader.Value.ToString() == "result")
+                    {
+                        jsonReader.Read();
+                        Result = jsonReader.Value.ToString();
+                    }
+
+                    else if (jsonReader.Value.ToString() == "tag")
+                    {
+                        jsonReader.Read();
+                        Tag = jsonReader.Value != null ? (int?)Convert.ToInt32(jsonReader.Value) : null;
+                    }
+
+                    else if (jsonReader.Value.ToString() == "arguments")
+                    {
+                        jsonReader.Read();
+                        Arguments = JObject.Load(jsonReader);
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/Transmission.API.RPC/Entity/BandwidthGroup.cs b/Transmission.API.RPC/Entity/BandwidthGroup.cs
index df48db6..3ad96de 100644
--- a/Transmission.API.RPC/Entity/BandwidthGroup.cs
+++ b/Transmission.API.RPC/Entity/BandwidthGroup.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json;
+#nullable enable
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Text;
@@ -20,7 +21,7 @@ public class BandwidthGroup
         /// Name of the bandwidth group
         /// </summary>
         [JsonProperty("name")]
-        public string Name { get; set; }
+        public string? Name { get; set; }
 
         /// <summary>
         /// Max global download speed of this bandwidth group (KBps)
diff --git a/Transmission.API.RPC/Entity/FreeSpace.cs b/Transmission.API.RPC/Entity/FreeSpace.cs
index 71b80b2..5addb32 100644
--- a/Transmission.API.RPC/Entity/FreeSpace.cs
+++ b/Transmission.API.RPC/Entity/FreeSpace.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json;
+#nullable enable
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -16,7 +17,7 @@ public class FreeSpace
         /// Path of the queried directory
         /// </summary>
         [JsonProperty("path")]
-        public string Path { get; set; }
+        public string? Path { get; set; }
 
         /// <summary>
         /// The size, in bytes, of the free space in that directory
diff --git a/Transmission.API.RPC/Entity/IntOrArrayConverter.cs b/Transmission.API.RPC/Entity/IntOrArrayConverter.cs
index 64b3b8b..9175648 100644
--- a/Transmission.API.RPC/Entity/IntOrArrayConverter.cs
+++ b/Transmission.API.RPC/Entity/IntOrArrayConverter.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json.Linq;
+#nullable enable
+using Newtonsoft.Json.Linq;
 using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
@@ -22,7 +23,7 @@ public class IntOrArrayConverter : JsonConverter
         /// <returns></returns>
         public override bool CanConvert(Type objectType)
         {
-            return objectType == typeof(long[]) || objectType == typeof(long) || objectType == typeof(int[]) || objectType == typeof(int) || objectType == typeof(long?[]) || objectType == typeof(long?) || objectType == typeof(int?[]) || objectType == typeof(int?);
+            return objectType == typeof(long?[]) || objectType == typeof(long[]) || objectType == typeof(int?[]) || objectType == typeof(int[]) || objectType == typeof(long?) || objectType == typeof(long) || objectType == typeof(int?) || objectType == typeof(int);
         }
 
         /// <summary>
@@ -34,7 +35,7 @@ public override bool CanConvert(Type objectType)
         /// <param name="serializer"></param>
         /// <returns></returns>
         /// <exception cref="JsonSerializationException"></exception>
-        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+        public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
         {
             JToken token = JToken.Load(reader);
 
@@ -60,11 +61,11 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
         /// <param name="writer"></param>
         /// <param name="value"></param>
         /// <param name="serializer"></param>
-        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+        public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
         {
             if (value != null)
             {
-                long?[] longArray = (long?[])value;
+                long[] longArray = (long[])value;
                 if (longArray.Length == 1)
                 {
                     writer.WriteValue(longArray[0]);
diff --git a/Transmission.API.RPC/Entity/NewTorrentInfo.cs b/Transmission.API.RPC/Entity/NewTorrentInfo.cs
index d3e82be..9be24bf 100644
--- a/Transmission.API.RPC/Entity/NewTorrentInfo.cs
+++ b/Transmission.API.RPC/Entity/NewTorrentInfo.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json;
+#nullable enable
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -16,24 +17,24 @@ public class NewTorrentInfo
 		/// Torrent ID
 		/// </summary>
 		[JsonProperty("id")]
-		public int ID { get; set; }
+		public int Id { get; set; }
 
 		/// <summary>
 		/// Torrent name
 		/// </summary>
 		[JsonProperty("name")]
-		public string Name { get; set; }
+		public string? Name { get; set; }
 
 		/// <summary>
 		/// Torrent Hash
 		/// </summary>
 		[JsonProperty("hashString")]
-		public string HashString { get; set; }
+		public string? HashString { get; set; }
 
         /// <summary>
         /// Whether the torrent is a duplicate of an existing torrent (add failed)
         /// </summary>
-        public bool Duplicate { get; set; }
+        public bool? Duplicate { get; set; }
 
 	}
 }
diff --git a/Transmission.API.RPC/Entity/RenameTorrentInfo.cs b/Transmission.API.RPC/Entity/RenameTorrentInfo.cs
index 7365f4e..e26ecf8 100644
--- a/Transmission.API.RPC/Entity/RenameTorrentInfo.cs
+++ b/Transmission.API.RPC/Entity/RenameTorrentInfo.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json;
+#nullable enable
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -16,18 +17,18 @@ public class RenameTorrentInfo
         /// The torrent's unique Id.
         /// </summary>
         [JsonProperty("id")]
-        public int ID { get; set; }
+        public int Id { get; set; }
 
 		/// <summary>
 		/// File path.
 		/// </summary>
 		[JsonProperty("path")]
-		public string Path { get; set; }
+		public string? Path { get; set; }
 
 		/// <summary>
 		/// File name.
 		/// </summary>
 		[JsonProperty("name")]
-		public string Name { get; set; }
+		public string? Name { get; set; }
 	}
 }
diff --git a/Transmission.API.RPC/Entity/SessionInfo.cs b/Transmission.API.RPC/Entity/SessionInfo.cs
index 2b4fba3..c75eb04 100644
--- a/Transmission.API.RPC/Entity/SessionInfo.cs
+++ b/Transmission.API.RPC/Entity/SessionInfo.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json;
+#nullable enable
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -60,7 +61,7 @@ public class SessionInfo
         /// Location of the blocklist to use for "blocklist-update"
         /// </summary>
         [JsonProperty("blocklist-url")]
-        public string BlocklistUrl { get; set; }
+        public string? BlocklistUrl { get; set; }
 
         /// <summary>
         /// True means enabled
@@ -84,7 +85,7 @@ public class SessionInfo
         /// Default announce URLs, one per line, and a blank line between tiers
         /// </summary>
         [JsonProperty("default-trackers")]
-        public string DefaultTrackers { get; set; }
+        public string? DefaultTrackers { get; set; }
 
         /// <summary>
         /// Allow DHT in public torrents
@@ -96,14 +97,14 @@ public class SessionInfo
         /// Default path to download torrents
         /// </summary>
         [JsonProperty("download-dir")]
-        public string DownloadDirectory { get; set; }
+        public string? DownloadDirectory { get; set; }
 
         /// <summary>
         /// Free space in download dir
         /// </summary>
         [JsonProperty("download-dir-free-space")]
         [Obsolete("Obsolete since Transmission 4.0.0. Use the free-space method instead.")]
-        public string DownloadDirectoryFreeSpace { get; set; }
+        public string? DownloadDirectoryFreeSpace { get; set; }
 
         /// <summary>
         /// Max number of torrents to download at once (see download-queue-enabled)
@@ -121,7 +122,7 @@ public class SessionInfo
         /// "required", "preferred", "tolerated"
         /// </summary>
         [JsonProperty("encryption")]
-        public string Encryption { get; set; }
+        public string? Encryption { get; set; }
 
         /// <summary>
         /// Torrents we're seeding will be stopped if they're idle for this long
@@ -139,7 +140,7 @@ public class SessionInfo
         /// Path for incomplete torrents, when enabled
         /// </summary>
         [JsonProperty("incomplete-dir")]
-        public string IncompleteDirectory { get; set; }
+        public string? IncompleteDirectory { get; set; }
 
         /// <summary>
         /// True means keep torrents in incomplete-dir until done
@@ -211,13 +212,13 @@ public class SessionInfo
         /// Session ID
         /// </summary>
         [JsonProperty("session-id")]
-        public string SessionId { get; set; }
+        public string? SessionId { get; set; }
 
         /// <summary>
         /// Filename of the script to run
         /// </summary>
         [JsonProperty("script-torrent-added-filename")]
-        public string ScriptTorrentAddedFilename { get; set; }
+        public string? ScriptTorrentAddedFilename { get; set; }
 
         /// <summary>
         /// Whether or not to call the "added" script
@@ -229,7 +230,7 @@ public class SessionInfo
         /// Filename of the script to run
         /// </summary>
         [JsonProperty("script-torrent-done-filename")]
-        public string ScriptTorrentDoneFilename { get; set; }
+        public string? ScriptTorrentDoneFilename { get; set; }
 
         /// <summary>
         /// Whether or not to call the "done" script
@@ -241,7 +242,7 @@ public class SessionInfo
         /// Filename of the script to run
         /// </summary>
         [JsonProperty("script-torrent-done-seeding-filename")]
-        public string ScriptTorrentDoneSeedingFilename { get; set; }
+        public string? ScriptTorrentDoneSeedingFilename { get; set; }
 
         /// <summary>
         /// Whether or not to call the "done seeding" script
@@ -313,7 +314,7 @@ public class SessionInfo
         /// Units
         /// </summary>
         [JsonProperty("units")]
-        public Units Units { get; set; }
+        public Units? Units { get; set; }
 
         /// <summary>
         /// True means allow utp
@@ -325,7 +326,7 @@ public class SessionInfo
         /// Location of transmission's configuration directory
         /// </summary>
         [JsonProperty("config-dir")]
-        public string ConfigDirectory{ get; set; }
+        public string? ConfigDirectory{ get; set; }
 
         /// <summary>
         /// The current RPC API version
@@ -343,12 +344,12 @@ public class SessionInfo
         /// Current RPC API version in a semver-compatible string
         /// </summary>
         [JsonProperty("rpc-version-semver")]
-        public string RpcVersionSemver { get; set; }
+        public string? RpcVersionSemver { get; set; }
 
         /// <summary>
         /// long? version string "$version ($revision)"
         /// </summary>
         [JsonProperty("version")]
-        public string Version{ get; set; }
+        public string? Version{ get; set; }
     }
 }
diff --git a/Transmission.API.RPC/Entity/Statistic.cs b/Transmission.API.RPC/Entity/Statistic.cs
index d54e36b..b63ca66 100644
--- a/Transmission.API.RPC/Entity/Statistic.cs
+++ b/Transmission.API.RPC/Entity/Statistic.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json;
+#nullable enable
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -46,13 +47,13 @@ public class Statistic
         /// Cumulative stats
         /// </summary>
         [JsonProperty("cumulative-stats")]
-        public CommonStatistic CumulativeStats { get; set; }
+        public CommonStatistic? CumulativeStats { get; set; }
  
         /// <summary>
         /// Current stats
         /// </summary>
         [JsonProperty("current-stats")]
-        public CommonStatistic CurrentStats { get; set; }
+        public CommonStatistic? CurrentStats { get; set; }
     }
 
     /// <summary>
diff --git a/Transmission.API.RPC/Entity/TorrentInfo.cs b/Transmission.API.RPC/Entity/TorrentInfo.cs
index d18ee1e..4907b25 100644
--- a/Transmission.API.RPC/Entity/TorrentInfo.cs
+++ b/Transmission.API.RPC/Entity/TorrentInfo.cs
@@ -1,4 +1,5 @@
-using JDRemote.Backends.Transmission;
+#nullable enable
+using JDRemote.Backends.Transmission;
 using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
@@ -17,7 +18,7 @@ public class TorrentInfo
         /// The torrent's unique Id.
         /// </summary>
         [JsonProperty("id")]
-        public long? Id { get; set; }
+        public long Id { get; set; }
 
         /// <summary>
         /// Activity date
@@ -36,7 +37,7 @@ public class TorrentInfo
         /// </summary>
         [JsonProperty("availability")]
         [JsonConverter(typeof(IntOrArrayConverter))] // Without this converter, Transmission < 4.0.0 leads to an error.
-        public long?[] Availability { get; set; }
+        public long[]? Availability { get; set; }
 
         /// <summary>
         /// Torrents bandwidth priority
@@ -48,7 +49,7 @@ public class TorrentInfo
         /// Comment
         /// </summary>
         [JsonProperty("comment")]
-        public string Comment { get; set; }
+        public string? Comment { get; set; }
 
         /// <summary>
         /// Corrupt ever
@@ -60,7 +61,7 @@ public class TorrentInfo
         /// Creator
         /// </summary>
         [JsonProperty("creator")]
-        public string Creator { get; set; }
+        public string? Creator { get; set; }
 
         /// <summary>
         /// Date created
@@ -84,7 +85,7 @@ public class TorrentInfo
         /// Download directory
         /// </summary>
         [JsonProperty("downloadDir")]
-        public string DownloadDir { get; set; }
+        public string? DownloadDir { get; set; }
 
         /// <summary>
         /// Downloaded ever
@@ -120,7 +121,7 @@ public class TorrentInfo
         /// Error string
         /// </summary>
         [JsonProperty("errorString")]
-        public string ErrorString { get; set; }
+        public string? ErrorString { get; set; }
 
         /// <summary>
         /// ETA
@@ -144,25 +145,25 @@ public class TorrentInfo
         /// Files
         /// </summary>
         [JsonProperty("files")]
-        public TransmissionTorrentFiles[] Files { get; set; }
+        public TransmissionTorrentFiles[]? Files { get; set; }
 
         /// <summary>
         /// File stats
         /// </summary>
         [JsonProperty("fileStats")]
-        public TransmissionTorrentFileStats[] FileStats { get; set; }
+        public TransmissionTorrentFileStats[]? FileStats { get; set; }
 
         /// <summary>
         /// Group
         /// </summary>
         [JsonProperty("group")]
-        public string Group { get; set; }
+        public string? Group { get; set; }
 
         /// <summary>
         /// Hash string
         /// </summary>
         [JsonProperty("hashString")]
-        public string HashString { get; set; }
+        public string? HashString { get; set; }
 
         /// <summary>
         /// Have unchecked
@@ -204,7 +205,7 @@ public class TorrentInfo
         /// Labels
         /// </summary>
         [JsonProperty("labels")]
-        public string[] Labels { get; set; }
+        public string[]? Labels { get; set; }
 
         /// <summary>
         /// Left until done
@@ -216,7 +217,7 @@ public class TorrentInfo
         /// Magnet link
         /// </summary>
         [JsonProperty("magnetLink")]
-        public string MagnetLink { get; set; }
+        public string? MagnetLink { get; set; }
 
         /// <summary>
         /// Manual announce time
@@ -240,7 +241,7 @@ public class TorrentInfo
         /// Name
         /// </summary>
         [JsonProperty("name")]
-        public string Name { get; set; }
+        public string? Name { get; set; }
 
         /// <summary>
         /// Peer limit
@@ -252,7 +253,7 @@ public class TorrentInfo
         /// Peers
         /// </summary>
         [JsonProperty("peers")]
-        public TransmissionTorrentPeers[] Peers { get; set; }
+        public TransmissionTorrentPeers[]? Peers { get; set; }
 
         /// <summary>
         /// Peers connected
@@ -264,7 +265,7 @@ public class TorrentInfo
         /// Peers from
         /// </summary>
         [JsonProperty("peersFrom")]
-        public TransmissionTorrentPeersFrom PeersFrom { get; set; }
+        public TransmissionTorrentPeersFrom? PeersFrom { get; set; }
 
         /// <summary>
         /// Peers getting from us
@@ -294,7 +295,7 @@ public class TorrentInfo
         /// Pieces
         /// </summary>
         [JsonProperty("pieces")]
-        public string Pieces { get; set; }
+        public string? Pieces { get; set; }
 
         /// <summary>
         /// Piece count
@@ -312,13 +313,13 @@ public class TorrentInfo
         /// Priorities
         /// </summary>
         [JsonProperty("priorities")]
-        public long?[] Priorities { get; set; }
+        public long[]? Priorities { get; set; }
 
         /// <summary>
         /// Primary mime type
         /// </summary>
         [JsonProperty("primary-mime-type")]
-        public string PrimaryMimeType { get; set; }
+        public string? PrimaryMimeType { get; set; }
 
         /// <summary>
         /// Queue position
@@ -408,7 +409,7 @@ public class TorrentInfo
         /// Trackers
         /// </summary>
         [JsonProperty("trackers")]
-        public TransmissionTorrentTrackers[] Trackers { get; set; }
+        public TransmissionTorrentTrackers[]? Trackers { get; set; }
 
         /// <summary>
         /// Tracker list:
@@ -416,13 +417,13 @@ public class TorrentInfo
         /// line between tiers
         /// </summary>
         [JsonProperty("trackerList")]
-        public string TrackerList { get; set; }
+        public string? TrackerList { get; set; }
 
         /// <summary>
         /// Tracker stats
         /// </summary>
         [JsonProperty("trackerStats")]
-        public TransmissionTorrentTrackerStats[] TrackerStats { get; set; }
+        public TransmissionTorrentTrackerStats[]? TrackerStats { get; set; }
 
         /// <summary>
         /// Total size
@@ -434,7 +435,7 @@ public class TorrentInfo
         /// Torrent file
         /// </summary>
         [JsonProperty("torrentFile")]
-        public string TorrentFile { get; set; }
+        public string? TorrentFile { get; set; }
 
         /// <summary>
         /// Uploaded ever
@@ -464,13 +465,13 @@ public class TorrentInfo
         /// Wanted
         /// </summary>
         [JsonProperty("wanted")]
-        public bool?[] Wanted { get; set; }
+        public bool[]? Wanted { get; set; }
 
         /// <summary>
         /// Web seeds
         /// </summary>
         [JsonProperty("webseeds")]
-        public string[] Webseeds { get; set; }
+        public string[]? Webseeds { get; set; }
 
         /// <summary>
         /// Web seeds sending to us
@@ -500,7 +501,7 @@ public class TransmissionTorrentFiles
         /// Name
         /// </summary>
         [JsonProperty("name")]
-        public string Name{ get; set; }
+        public string? Name{ get; set; }
 
         /// <summary>
         /// First piece index of file
@@ -548,13 +549,13 @@ public class TransmissionTorrentPeers
         /// Address
         /// </summary>
         [JsonProperty("address")]
-        public string Address{ get; set; }
+        public string? Address{ get; set; }
 
         /// <summary>
         /// Client name
         /// </summary>
         [JsonProperty("clientName")]
-        public string ClientName{ get; set; }
+        public string? ClientName{ get; set; }
 
         /// <summary>
         /// Client is choked
@@ -572,7 +573,7 @@ public class TransmissionTorrentPeers
         /// Flag string
         /// </summary>
         [JsonProperty("flagStr")]
-        public string FlagStr{ get; set; }
+        public string? FlagStr{ get; set; }
 
         /// <summary>
         /// Is downloading from
@@ -698,25 +699,25 @@ public class TransmissionTorrentTrackers
         /// Announce
         /// </summary>
         [JsonProperty("announce")]
-        public string Announce{ get; set; }
+        public string? Announce{ get; set; }
 
         /// <summary>
         /// Id
         /// </summary>
         [JsonProperty("id")]
-        public long? Id{ get; set; }
+        public long Id{ get; set; }
 
         /// <summary>
         /// Scrape
         /// </summary>
         [JsonProperty("scrape")]
-        public string Scrape{ get; set; }
+        public string? Scrape{ get; set; }
 
         /// <summary>
         /// Site name
         /// </summary>
         [JsonProperty("sitename")]
-        public string SiteName { get; set; }
+        public string? SiteName { get; set; }
 
         /// <summary>
         /// Tier
@@ -734,7 +735,7 @@ public class TransmissionTorrentTrackerStats
         /// Announce
         /// </summary>
         [JsonProperty("announce")]
-        public string Announce{ get; set; }
+        public string? Announce{ get; set; }
 
         /// <summary>
         /// Announce state
@@ -764,7 +765,7 @@ public class TransmissionTorrentTrackerStats
         /// Host
         /// </summary>
         [JsonProperty("host")]
-        public string Host{ get; set; }
+        public string? Host{ get; set; }
 
         /// <summary>
         /// Is backup
@@ -788,7 +789,7 @@ public class TransmissionTorrentTrackerStats
         /// Last announce result 
         /// </summary>
         [JsonProperty("lastAnnounceResult")]
-        public string LastAnnounceResult{ get; set; }
+        public string? LastAnnounceResult{ get; set; }
 
         /// <summary>
         /// Last announce succeeded
@@ -806,7 +807,7 @@ public class TransmissionTorrentTrackerStats
         /// Last scrape result
         /// </summary>
         [JsonProperty("lastScrapeResult")]
-        public string LastScrapeResult{ get; set; }
+        public string? LastScrapeResult{ get; set; }
 
         /// <summary>
         /// Last announce timed out
@@ -848,7 +849,7 @@ public class TransmissionTorrentTrackerStats
         /// Scrape
         /// </summary>
         [JsonProperty("scrape")]
-        public string Scrape{ get; set; }
+        public string? Scrape{ get; set; }
 
         /// <summary>
         /// Tier
@@ -890,11 +891,11 @@ public class TransmissionTorrentTrackerStats
         /// Site name
         /// </summary>
         [JsonProperty("sitename")]
-        public string SiteName { get; set; }
+        public string? SiteName { get; set; }
     }
 
     /// <summary>
-    /// Contains arrays of torrents and removed torrents
+    /// Contains arrays of torrents, and removed torrents in an integer array
     /// </summary>
     public class TransmissionTorrents
     {
@@ -902,12 +903,12 @@ public class TransmissionTorrents
         /// Array of torrents
         /// </summary>
         [JsonProperty("torrents")]
-        public TorrentInfo[] Torrents{ get; set; }
+        public TorrentInfo[]? Torrents{ get; set; }
 
         /// <summary>
         /// Array of torrent-id numbers of recently-removed torrents
         /// </summary>
         [JsonProperty("removed")]
-        public long?[] Removed{ get; set; }
+        public long[]? Removed{ get; set; }
     }
 }
diff --git a/Transmission.API.RPC/Entity/TorrentInfoConverter.cs b/Transmission.API.RPC/Entity/TorrentInfoConverter.cs
new file mode 100644
index 0000000..de89d33
--- /dev/null
+++ b/Transmission.API.RPC/Entity/TorrentInfoConverter.cs
@@ -0,0 +1,142 @@
+#nullable enable
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace Transmission.API.RPC.Entity
+{
+    // Credits to ChatGPT
+    /// <summary>
+    /// Converts a table (array of arrays) result from torrent-get into TransmissionTorrents in a reasonably performant way
+    /// </summary>
+    public static class TorrentInfoConverter
+    {
+        private readonly static Dictionary<string, PropertyInfo> _propertyMap = CreatePropertyMap(typeof(TorrentInfo));
+
+        // Method to create the property map for TorrentInfo
+        private static Dictionary<string, PropertyInfo> CreatePropertyMap(Type type)
+        {
+            return type.GetProperties()
+                .Where(prop => prop.GetCustomAttributes(typeof(JsonPropertyAttribute), false).Any())
+                .ToDictionary(
+                    prop => prop.GetCustomAttributes(typeof(JsonPropertyAttribute), false)
+                                .Cast<JsonPropertyAttribute>()
+                                .First()
+                                .PropertyName!,
+                    prop => prop);
+        }
+
+        public static TransmissionTorrents ConvertFromJObject(JObject jObject)
+        {
+            // Extract and convert the "torrents" field
+            var torrentsArray = jObject["torrents"] as JArray;
+            if (torrentsArray == null || torrentsArray.Count == 0)
+            {
+                return new TransmissionTorrents
+                {
+                    Torrents = Array.Empty<TorrentInfo>(),
+                    Removed = GetRemoved(jObject)
+                };
+            }
+
+            var fieldNames = torrentsArray[0].ToObject<string[]>();
+            var torrents = new TorrentInfo[torrentsArray.Count - 1];
+
+            for (int i = 1; i < torrentsArray.Count; i++)
+            {
+                var row = torrentsArray[i].ToObject<object[]>();
+                if (fieldNames != null & row != null) torrents[i - 1] = CreateTorrentInfo(fieldNames!, row!);
+            }
+
+            return new TransmissionTorrents
+            {
+                Torrents = torrents,
+                Removed = GetRemoved(jObject)
+            };
+        }
+
+        private static TorrentInfo CreateTorrentInfo(string[] fieldNames, object[] row)
+        {
+            var torrentInfo = new TorrentInfo();
+
+            for (int j = 0; j < fieldNames.Length; j++)
+            {
+                var fieldName = fieldNames[j];
+                var value = row[j];
+
+                if (_propertyMap.TryGetValue(fieldName, out var property))
+                {
+                    try
+                    {
+                        var convertedValue = ConvertValue(value, property.PropertyType);
+                        property.SetValue(torrentInfo, convertedValue);
+                    }
+                    catch (Exception ex)
+                    {
+                        Console.WriteLine($"Failed to set property '{property.Name}': {ex.Message}");
+                    }
+                }
+            }
+
+            return torrentInfo;
+        }
+
+        private static object? ConvertValue(object value, Type targetType)
+        {
+            if (value == null)
+            {
+                return null;
+            }
+
+            // Handle specific types directly for efficiency
+            if (targetType == typeof(long?)) return value is long l ? (long?)l : null;
+            if (targetType == typeof(double?)) return value is double d ? (double?)d : null;
+            if (targetType == typeof(bool?)) return value is bool b ? (bool?)b : null;
+            if (targetType == typeof(string)) return value?.ToString();
+
+            if (targetType.IsArray)
+            {
+                var elementType = targetType.GetElementType();
+                var valueArray = value as JArray;
+                if (valueArray != null)
+                {
+                    var convertedArray = Array.CreateInstance(elementType, valueArray.Count);
+
+                    for (int i = 0; i < valueArray.Count; i++)
+                    {
+                        convertedArray.SetValue(ConvertValue(valueArray[i], elementType), i);
+                    }
+                    return convertedArray;
+                }
+                else return null;
+            }
+
+            // Handle complex nested objects
+            if (targetType.IsClass && targetType != typeof(string))
+            {
+                var nestedJObject = value as JObject;
+                if (nestedJObject != null)
+                {
+                    return nestedJObject.ToObject(targetType);
+                }
+            }
+
+            return Convert.ChangeType(value, targetType);
+        }
+
+        private static long[]? GetRemoved(JObject jObject)
+        {
+            var removedArray = jObject["removed"] as JArray;
+            if (removedArray == null)
+            {
+                return null;
+            }
+
+            return removedArray.ToObject<long[]>();
+        }
+    }
+}
diff --git a/Transmission.API.RPC/Entity/Units.cs b/Transmission.API.RPC/Entity/Units.cs
index 18aa269..faf6766 100644
--- a/Transmission.API.RPC/Entity/Units.cs
+++ b/Transmission.API.RPC/Entity/Units.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json;
+#nullable enable
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -16,7 +17,7 @@ public class Units
         /// Speed units
         /// </summary>
         [JsonProperty("speed-units")]
-        public string[] SpeedUnits { get; set; }
+        public string[]? SpeedUnits { get; set; }
 
         /// <summary>
         /// Speed bytes
@@ -28,7 +29,7 @@ public class Units
         /// Size units
         /// </summary>
         [JsonProperty("size-units")]
-        public string[] SizeUnits { get; set; }
+        public string[]? SizeUnits { get; set; }
 
         /// <summary>
         /// Size bytes
@@ -40,7 +41,7 @@ public class Units
         /// Memory units
         /// </summary>
         [JsonProperty("memory-units")]
-        public string[] MemoryUnits { get; set; }
+        public string[]? MemoryUnits { get; set; }
 
         /// <summary>
         /// Memory bytes
diff --git a/Transmission.API.RPC/Exceptions.cs b/Transmission.API.RPC/Exceptions.cs
new file mode 100644
index 0000000..b8ce25e
--- /dev/null
+++ b/Transmission.API.RPC/Exceptions.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Transmission.API.RPC
+{
+    public class RequestFailedException : Exception
+    {
+        public RequestFailedException(string description) : base(description) { }
+    }
+
+    public class SessionIdException : Exception
+    {
+        public SessionIdException() : base("Session ID Error") { }
+    }
+}
diff --git a/Transmission.API.RPC/ITransmissionClient.cs b/Transmission.API.RPC/ITransmissionClient.cs
index 911dc62..b32be98 100644
--- a/Transmission.API.RPC/ITransmissionClient.cs
+++ b/Transmission.API.RPC/ITransmissionClient.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+#nullable enable
+using System.Threading.Tasks;
 using System;
 using Transmission.API.RPC.Arguments;
 using Transmission.API.RPC.Entity;
@@ -13,7 +14,7 @@ public interface ITransmissionClient
         /// <summary>
         /// Current tag
         /// </summary>
-        int CurrentTag { get; }
+        long CurrentTag { get; }
 
         /// <summary>
         /// Session ID
@@ -29,7 +30,7 @@ public interface ITransmissionClient
         /// Update blocklist (API: blocklist-update)
         /// </summary>
         /// <returns>Blocklist size</returns>
-        int BlocklistUpdate();
+        long? BlocklistUpdate();
 
         /// <summary>
         /// Close current session (API: session-close)
@@ -40,32 +41,32 @@ public interface ITransmissionClient
         /// Get free space is available in a client-specified folder.
         /// </summary>
         /// <param name="path">The directory to query</param>
-        FreeSpace FreeSpace(string path);
+        FreeSpace? FreeSpace(string path);
 
         /// <summary>
         /// Get information of current session (API: session-get)
         /// </summary>
         /// <returns>Session information</returns>
-        SessionInfo GetSessionInformation();
+        SessionInfo? GetSessionInformation();
 
         /// <summary>
         /// Get information of current session (API: session-get)
         /// </summary>
 		/// <param name="fields">Optional fields of session information</param>
         /// <returns>Session information</returns>
-        SessionInfo GetSessionInformation(string[] fields);
+        SessionInfo? GetSessionInformation(string[] fields);
 
         /// <summary>
         /// Get session stat
         /// </summary>
         /// <returns>Session stat</returns>
-        Statistic GetSessionStatistic();
+        Statistic? GetSessionStatistic();
 
         /// <summary>
         /// See if your incoming peer port is accessible from the outside world (API: port-test)
         /// </summary>
         /// <returns>A Tuple with a boolean of whether the port test succeeded, and a PortTestProtocol enum of which protocol was used for the test</returns>
-        Tuple<bool, PortTestProtocol> PortTest();
+        Tuple<bool?, PortTestProtocol> PortTest();
 
         /// <summary>
         /// Set information to current session (API: session-set)
@@ -77,53 +78,55 @@ public interface ITransmissionClient
         /// Add torrent (API: torrent-add)
         /// </summary>
         /// <returns>Torrent info (ID, Name and HashString)</returns>
-        NewTorrentInfo TorrentAdd(NewTorrent torrent);
+        NewTorrentInfo? TorrentAdd(NewTorrent torrent);
 
         /// <summary>
         /// Get fields of recently active torrents (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
+        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <returns>Torrents info</returns>
-        TransmissionTorrents TorrentGetRecentlyActive(string[] fields);
+        TransmissionTorrents? TorrentGetRecentlyActive(string[] fields, bool asObjects = false);
 
         /// <summary>
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
+        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
         /// <returns>Torrents info</returns>
-        TransmissionTorrents TorrentGet(string[] fields, params int[] ids);
+        TransmissionTorrents? TorrentGet(string[] fields, bool asObjects = false, params long[] ids);
 
         /// <summary>
         /// Move torrents to bottom in queue  (API: queue-move-bottom)
         /// </summary>
         /// <param name="ids"></param>
-        void TorrentQueueMoveBottom(int[] ids);
+        void TorrentQueueMoveBottom(long[] ids);
 
         /// <summary>
         /// Move down torrents in queue (API: queue-move-down)
         /// </summary>
         /// <param name="ids"></param>
-        void TorrentQueueMoveDown(int[] ids);
+        void TorrentQueueMoveDown(long[] ids);
 
         /// <summary>
         /// Move torrents in queue on top (API: queue-move-top)
         /// </summary>
         /// <param name="ids">Torrents id</param>
-        void TorrentQueueMoveTop(int[] ids);
+        void TorrentQueueMoveTop(long[] ids);
 
         /// <summary>
         /// Move up torrents in queue (API: queue-move-up)
         /// </summary>
         /// <param name="ids"></param>
-        void TorrentQueueMoveUp(int[] ids);
+        void TorrentQueueMoveUp(long[] ids);
 
         /// <summary>
         /// Remove torrents (API: torrent-remove)
         /// </summary>
         /// <param name="ids">Torrents id</param>
         /// <param name="deleteData">Remove local data</param>
-        void TorrentRemove(int[] ids, bool deleteData = false);
+        void TorrentRemove(long[] ids, bool deleteData = false);
 
         /// <summary>
         /// Rename a file or directory in a torrent (API: torrent-rename-path)
@@ -131,7 +134,7 @@ public interface ITransmissionClient
         /// <param name="id">The torrent whose path will be renamed</param>
         /// <param name="path">The path to the file or folder that will be renamed</param>
         /// <param name="name">The file or folder's new name</param>
-        RenameTorrentInfo TorrentRenamePath(int id, string path, string name);
+        RenameTorrentInfo? TorrentRenamePath(long id, string path, string name);
 
         /// <summary>
         /// Set torrent params (API: torrent-set)
@@ -145,7 +148,7 @@ public interface ITransmissionClient
         /// <param name="ids">Torrent ids</param>
         /// <param name="location">The new torrent location</param>
         /// <param name="move">Move from previous location</param>
-        void TorrentSetLocation(int[] ids, string location, bool move);
+        void TorrentSetLocation(long[] ids, string location, bool move);
 
         /// <summary>
         /// Start recently active torrents (API: torrent-start)
@@ -206,14 +209,14 @@ public interface ITransmissionClient
         /// Get bandwidth groups (API: group-get)
         /// </summary>
         /// <returns></returns>
-        BandwidthGroup[] BandwidthGroupGet();
+        BandwidthGroup[]? BandwidthGroupGet();
 
         /// <summary>
         /// Get bandwidth groups (API: group-get)
         /// </summary>
         /// <param name="groups">Optional names of groups to get</param>
         /// <returns></returns>
-        BandwidthGroup[] BandwidthGroupGet(string[] groups);
+        BandwidthGroup[]? BandwidthGroupGet(string[] groups);
 
         /// <summary>
         /// Set bandwidth groups (API: group-set)
diff --git a/Transmission.API.RPC/ITransmissionClientAsync.cs b/Transmission.API.RPC/ITransmissionClientAsync.cs
index 192bba7..e0f0cda 100644
--- a/Transmission.API.RPC/ITransmissionClientAsync.cs
+++ b/Transmission.API.RPC/ITransmissionClientAsync.cs
@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using System.Threading.Tasks;
 using Transmission.API.RPC.Arguments;
 using Transmission.API.RPC.Entity;
@@ -15,7 +16,7 @@ public interface ITransmissionClientAsync
         /// Update blocklist (API: blocklist-update)
         /// </summary>
         /// <returns>Blocklist size</returns>
-        Task<int> BlocklistUpdateAsync();
+        Task<long?> BlocklistUpdateAsync();
 
         /// <summary>
         /// Close current session (API: session-close)
@@ -26,32 +27,32 @@ public interface ITransmissionClientAsync
         /// Get free space is available in a client-specified folder.
         /// </summary>
         /// <param name="path">The directory to query</param>
-        Task<FreeSpace> FreeSpaceAsync(string path);
+        Task<FreeSpace?> FreeSpaceAsync(string path);
 
         /// <summary>
         /// Get information of current session (API: session-get)
         /// </summary>
         /// <returns>Session information</returns>
-        Task<SessionInfo> GetSessionInformationAsync();
+        Task<SessionInfo?> GetSessionInformationAsync();
 
         /// <summary>
         /// Get information of current session (API: session-get)
         /// </summary>
 		/// <param name="fields">Optional fields of session information</param>
         /// <returns>Session information</returns>
-        Task<SessionInfo> GetSessionInformationAsync(string[] fields);
+        Task<SessionInfo?> GetSessionInformationAsync(string[] fields);
 
         /// <summary>
         /// Get session stat
         /// </summary>
         /// <returns>Session stat</returns>
-        Task<Statistic> GetSessionStatisticAsync();
+        Task<Statistic?> GetSessionStatisticAsync();
 
         /// <summary>
         /// See if your incoming peer port is accessible from the outside world (API: port-test)
         /// </summary>
         /// <returns>A Tuple with a boolean of whether the port test succeeded, and a PortTestProtocol enum of which protocol was used for the test</returns>
-        Task<Tuple<bool, PortTestProtocol>> PortTestAsync();
+        Task<Tuple<bool?, PortTestProtocol>> PortTestAsync();
 
         /// <summary>
         /// Set information to current session (API: session-set)
@@ -63,53 +64,55 @@ public interface ITransmissionClientAsync
         /// Add torrent (API: torrent-add)
         /// </summary>
         /// <returns>Torrent info (ID, Name and HashString)</returns>
-        Task<NewTorrentInfo> TorrentAddAsync(NewTorrent torrent);
+        Task<NewTorrentInfo?> TorrentAddAsync(NewTorrent torrent);
 
         /// <summary>
         /// Get fields of recently active torrents (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
+        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <returns>Torrents info</returns>
-        Task<TransmissionTorrents> TorrentGetRecentlyActiveAsync(string[] fields);
+        Task<TransmissionTorrents?> TorrentGetRecentlyActiveAsync(string[] fields, bool asObjects = false);
 
         /// <summary>
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
+        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
         /// <returns>Torrents info</returns>
-        Task<TransmissionTorrents> TorrentGetAsync(string[] fields, params int[] ids);
+        Task<TransmissionTorrents?> TorrentGetAsync(string[] fields, bool asObjects = false, params long[] ids);
 
         /// <summary>
         /// Move torrents to bottom in queue  (API: queue-move-bottom)
         /// </summary>
         /// <param name="ids"></param>
-        Task TorrentQueueMoveBottomAsync(int[] ids);
+        Task TorrentQueueMoveBottomAsync(long[] ids);
 
         /// <summary>
         /// Move down torrents in queue (API: queue-move-down)
         /// </summary>
         /// <param name="ids"></param>
-        Task TorrentQueueMoveDownAsync(int[] ids);
+        Task TorrentQueueMoveDownAsync(long[] ids);
 
         /// <summary>
         /// Move torrents in queue on top (API: queue-move-top)
         /// </summary>
         /// <param name="ids">Torrents id</param>
-        Task TorrentQueueMoveTopAsync(int[] ids);
+        Task TorrentQueueMoveTopAsync(long[] ids);
 
         /// <summary>
         /// Move up torrents in queue (API: queue-move-up)
         /// </summary>
         /// <param name="ids"></param>
-        Task TorrentQueueMoveUpAsync(int[] ids);
+        Task TorrentQueueMoveUpAsync(long[] ids);
 
         /// <summary>
         /// Remove torrents
         /// </summary>
         /// <param name="ids">Torrents id</param>
         /// <param name="deleteData">Remove local data</param>
-        Task TorrentRemoveAsync(int[] ids, bool deleteData = false);
+        Task TorrentRemoveAsync(long[] ids, bool deleteData = false);
 
         /// <summary>
         /// Rename a file or directory in a torrent (API: torrent-rename-path)
@@ -117,7 +120,7 @@ public interface ITransmissionClientAsync
         /// <param name="id">The torrent whose path will be renamed</param>
         /// <param name="path">The path to the file or folder that will be renamed</param>
         /// <param name="name">The file or folder's new name</param>
-        Task<RenameTorrentInfo> TorrentRenamePathAsync(int id, string path, string name);
+        Task<RenameTorrentInfo?> TorrentRenamePathAsync(long id, string path, string name);
 
         /// <summary>
         /// Set torrent params (API: torrent-set)
@@ -131,7 +134,7 @@ public interface ITransmissionClientAsync
         /// <param name="ids">Torrent ids</param>
         /// <param name="location">The new torrent location</param>
         /// <param name="move">Move from previous location</param>
-        Task TorrentSetLocationAsync(int[] ids, string location, bool move);
+        Task TorrentSetLocationAsync(long[] ids, string location, bool move);
 
         /// <summary>
         /// Start recently active torrents (API: torrent-start)
@@ -192,14 +195,14 @@ public interface ITransmissionClientAsync
         /// Get bandwidth groups (API: group-get)
         /// </summary>
         /// <returns></returns>
-        Task<BandwidthGroup[]> BandwidthGroupGetAsync();
+        Task<BandwidthGroup[]?> BandwidthGroupGetAsync();
 
         /// <summary>
         /// Get bandwidth groups (API: group-get)
         /// </summary>
         /// <param name="groups">Optional names of groups to get</param>
         /// <returns></returns>
-        Task<BandwidthGroup[]> BandwidthGroupGetAsync(string[] groups);
+        Task<BandwidthGroup[]?> BandwidthGroupGetAsync(string[] groups);
 
         /// <summary>
         /// Set bandwidth groups (API: group-set)
diff --git a/Transmission.API.RPC/Transmission.API.RPC.csproj b/Transmission.API.RPC/Transmission.API.RPC.csproj
index 680474b..9dca373 100644
--- a/Transmission.API.RPC/Transmission.API.RPC.csproj
+++ b/Transmission.API.RPC/Transmission.API.RPC.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
     <PropertyGroup>
-        <TargetFrameworks>netstandard2.0</TargetFrameworks>
+        <TargetFrameworks>netstandard2.1</TargetFrameworks>
         <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
         <ApplicationIcon />
         <OutputType>Library</OutputType>

From a876f71ad60748bbd063ff78dfe9e8ff5c82e523 Mon Sep 17 00:00:00 2001
From: Jdbye <jdbye3@gmail.com>
Date: Mon, 10 Jun 2024 04:44:52 +0200
Subject: [PATCH 5/5] Removed table format support as it was not working
 correctly

It didn't read all the fields correctly and there was very minimal improvement in performance anyway.
---
 Transmission.API.RPC.Test/MethodsTest.cs      |   2 +-
 Transmission.API.RPC/Client.Async.cs          |  20 +--
 Transmission.API.RPC/Client.cs                |  10 +-
 .../Common/TransmissionResponse.cs            |   2 +-
 .../Entity/TorrentInfoConverter.cs            | 142 ------------------
 Transmission.API.RPC/ITransmissionClient.cs   |   6 +-
 .../ITransmissionClientAsync.cs               |   6 +-
 7 files changed, 14 insertions(+), 174 deletions(-)
 delete mode 100644 Transmission.API.RPC/Entity/TorrentInfoConverter.cs

diff --git a/Transmission.API.RPC.Test/MethodsTest.cs b/Transmission.API.RPC.Test/MethodsTest.cs
index 5f000d1..8a1e342 100644
--- a/Transmission.API.RPC.Test/MethodsTest.cs
+++ b/Transmission.API.RPC.Test/MethodsTest.cs
@@ -90,7 +90,7 @@ public void SetTorrentSettings_Test()
 
 			client.TorrentSet(settings);
 
-			torrentsInfo = client.TorrentGet(TorrentFields.ALL_FIELDS, false, torrentInfo.Id);
+			torrentsInfo = client.TorrentGet(TorrentFields.ALL_FIELDS, torrentInfo.Id);
 			torrentInfo = torrentsInfo.Torrents.FirstOrDefault();
 
 			Assert.IsFalse(trackerCount == torrentInfo.Trackers.Length);
diff --git a/Transmission.API.RPC/Client.Async.cs b/Transmission.API.RPC/Client.Async.cs
index 318c5b4..17a2d40 100644
--- a/Transmission.API.RPC/Client.Async.cs
+++ b/Transmission.API.RPC/Client.Async.cs
@@ -131,25 +131,19 @@ public async Task TorrentSetAsync(TorrentSettings settings)
         /// Get fields of recently active torrents (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
-        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <returns>Torrents info</returns>
-        public async Task<TransmissionTorrents?> TorrentGetRecentlyActiveAsync(string[] fields, bool asObjects = false)
+        public async Task<TransmissionTorrents?> TorrentGetRecentlyActiveAsync(string[] fields)
         {
             var arguments = new Dictionary<string, object>();
             arguments.Add("fields", fields);
             arguments.Add("ids", "recently-active");
 
-            if (asObjects) arguments.Add("format", "objects");
-            else arguments.Add("format", "table");
-
             var request = new TransmissionRequest("torrent-get", arguments);
             var response = await SendRequestAsync(request);
 
             if (response == null) return null;
 
-            TransmissionTorrents? torrents;
-            if (asObjects) torrents = response.Arguments.ToObject<TransmissionTorrents>();
-            else torrents = TorrentInfoConverter.ConvertFromJObject(response.Arguments);
+            TransmissionTorrents? torrents = response.Arguments.ToObject<TransmissionTorrents>();
 
             return torrents;
         }
@@ -158,17 +152,13 @@ public async Task TorrentSetAsync(TorrentSettings settings)
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
-        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
         /// <returns>Torrents info</returns>
-        public async Task<TransmissionTorrents?> TorrentGetAsync(string[] fields, bool asObjects = false, params long[] ids)
+        public async Task<TransmissionTorrents?> TorrentGetAsync(string[] fields, params long[] ids)
         {
             var arguments = new Dictionary<string, object>();
             arguments.Add("fields", fields);
 
-            if (asObjects) arguments.Add("format", "objects");
-            else arguments.Add("format", "table");
-
             if (ids != null && ids.Length > 0)
                 arguments.Add("ids", ids);
 
@@ -177,9 +167,7 @@ public async Task TorrentSetAsync(TorrentSettings settings)
 
             if (response == null) return null;
 
-            TransmissionTorrents? torrents;
-            if (asObjects) torrents = response?.Arguments.ToObject<TransmissionTorrents>();
-            else torrents = TorrentInfoConverter.ConvertFromJObject(response.Arguments);
+            TransmissionTorrents? torrents = response?.Arguments.ToObject<TransmissionTorrents>();
 
             return torrents;
         }
diff --git a/Transmission.API.RPC/Client.cs b/Transmission.API.RPC/Client.cs
index 3a765e3..dbe8018 100644
--- a/Transmission.API.RPC/Client.cs
+++ b/Transmission.API.RPC/Client.cs
@@ -150,11 +150,10 @@ public void TorrentSet(TorrentSettings settings)
         /// Get fields of recently active torrents (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
-        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <returns>Torrents info</returns>
-        public TransmissionTorrents TorrentGetRecentlyActive(string[] fields, bool asObjects = false)
+        public TransmissionTorrents TorrentGetRecentlyActive(string[] fields)
         {
-            var task = TorrentGetRecentlyActiveAsync(fields, asObjects);
+            var task = TorrentGetRecentlyActiveAsync(fields);
             task.WaitAndUnwrapException();
             return task.Result;
         }
@@ -163,12 +162,11 @@ public TransmissionTorrents TorrentGetRecentlyActive(string[] fields, bool asObj
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
-        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
         /// <returns>Torrents info</returns>
-        public TransmissionTorrents TorrentGet(string[] fields, bool asObjects = false, params long[] ids)
+        public TransmissionTorrents TorrentGet(string[] fields, params long[] ids)
         {
-            var task = TorrentGetAsync(fields, asObjects, ids);
+            var task = TorrentGetAsync(fields, ids);
             task.WaitAndUnwrapException();
             return task.Result;
         }
diff --git a/Transmission.API.RPC/Common/TransmissionResponse.cs b/Transmission.API.RPC/Common/TransmissionResponse.cs
index d3c29b7..1fbe997 100644
--- a/Transmission.API.RPC/Common/TransmissionResponse.cs
+++ b/Transmission.API.RPC/Common/TransmissionResponse.cs
@@ -22,7 +22,7 @@ public class TransmissionResponse
         /// <summary>
         /// Uniquely identifies which request this is a response to
         /// </summary>
-        public int? Tag;
+        public int? Tag { get; }
 
         /// <summary>
         /// Data
diff --git a/Transmission.API.RPC/Entity/TorrentInfoConverter.cs b/Transmission.API.RPC/Entity/TorrentInfoConverter.cs
deleted file mode 100644
index de89d33..0000000
--- a/Transmission.API.RPC/Entity/TorrentInfoConverter.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-#nullable enable
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-
-namespace Transmission.API.RPC.Entity
-{
-    // Credits to ChatGPT
-    /// <summary>
-    /// Converts a table (array of arrays) result from torrent-get into TransmissionTorrents in a reasonably performant way
-    /// </summary>
-    public static class TorrentInfoConverter
-    {
-        private readonly static Dictionary<string, PropertyInfo> _propertyMap = CreatePropertyMap(typeof(TorrentInfo));
-
-        // Method to create the property map for TorrentInfo
-        private static Dictionary<string, PropertyInfo> CreatePropertyMap(Type type)
-        {
-            return type.GetProperties()
-                .Where(prop => prop.GetCustomAttributes(typeof(JsonPropertyAttribute), false).Any())
-                .ToDictionary(
-                    prop => prop.GetCustomAttributes(typeof(JsonPropertyAttribute), false)
-                                .Cast<JsonPropertyAttribute>()
-                                .First()
-                                .PropertyName!,
-                    prop => prop);
-        }
-
-        public static TransmissionTorrents ConvertFromJObject(JObject jObject)
-        {
-            // Extract and convert the "torrents" field
-            var torrentsArray = jObject["torrents"] as JArray;
-            if (torrentsArray == null || torrentsArray.Count == 0)
-            {
-                return new TransmissionTorrents
-                {
-                    Torrents = Array.Empty<TorrentInfo>(),
-                    Removed = GetRemoved(jObject)
-                };
-            }
-
-            var fieldNames = torrentsArray[0].ToObject<string[]>();
-            var torrents = new TorrentInfo[torrentsArray.Count - 1];
-
-            for (int i = 1; i < torrentsArray.Count; i++)
-            {
-                var row = torrentsArray[i].ToObject<object[]>();
-                if (fieldNames != null & row != null) torrents[i - 1] = CreateTorrentInfo(fieldNames!, row!);
-            }
-
-            return new TransmissionTorrents
-            {
-                Torrents = torrents,
-                Removed = GetRemoved(jObject)
-            };
-        }
-
-        private static TorrentInfo CreateTorrentInfo(string[] fieldNames, object[] row)
-        {
-            var torrentInfo = new TorrentInfo();
-
-            for (int j = 0; j < fieldNames.Length; j++)
-            {
-                var fieldName = fieldNames[j];
-                var value = row[j];
-
-                if (_propertyMap.TryGetValue(fieldName, out var property))
-                {
-                    try
-                    {
-                        var convertedValue = ConvertValue(value, property.PropertyType);
-                        property.SetValue(torrentInfo, convertedValue);
-                    }
-                    catch (Exception ex)
-                    {
-                        Console.WriteLine($"Failed to set property '{property.Name}': {ex.Message}");
-                    }
-                }
-            }
-
-            return torrentInfo;
-        }
-
-        private static object? ConvertValue(object value, Type targetType)
-        {
-            if (value == null)
-            {
-                return null;
-            }
-
-            // Handle specific types directly for efficiency
-            if (targetType == typeof(long?)) return value is long l ? (long?)l : null;
-            if (targetType == typeof(double?)) return value is double d ? (double?)d : null;
-            if (targetType == typeof(bool?)) return value is bool b ? (bool?)b : null;
-            if (targetType == typeof(string)) return value?.ToString();
-
-            if (targetType.IsArray)
-            {
-                var elementType = targetType.GetElementType();
-                var valueArray = value as JArray;
-                if (valueArray != null)
-                {
-                    var convertedArray = Array.CreateInstance(elementType, valueArray.Count);
-
-                    for (int i = 0; i < valueArray.Count; i++)
-                    {
-                        convertedArray.SetValue(ConvertValue(valueArray[i], elementType), i);
-                    }
-                    return convertedArray;
-                }
-                else return null;
-            }
-
-            // Handle complex nested objects
-            if (targetType.IsClass && targetType != typeof(string))
-            {
-                var nestedJObject = value as JObject;
-                if (nestedJObject != null)
-                {
-                    return nestedJObject.ToObject(targetType);
-                }
-            }
-
-            return Convert.ChangeType(value, targetType);
-        }
-
-        private static long[]? GetRemoved(JObject jObject)
-        {
-            var removedArray = jObject["removed"] as JArray;
-            if (removedArray == null)
-            {
-                return null;
-            }
-
-            return removedArray.ToObject<long[]>();
-        }
-    }
-}
diff --git a/Transmission.API.RPC/ITransmissionClient.cs b/Transmission.API.RPC/ITransmissionClient.cs
index b32be98..ef46539 100644
--- a/Transmission.API.RPC/ITransmissionClient.cs
+++ b/Transmission.API.RPC/ITransmissionClient.cs
@@ -84,18 +84,16 @@ public interface ITransmissionClient
         /// Get fields of recently active torrents (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
-        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <returns>Torrents info</returns>
-        TransmissionTorrents? TorrentGetRecentlyActive(string[] fields, bool asObjects = false);
+        TransmissionTorrents? TorrentGetRecentlyActive(string[] fields);
 
         /// <summary>
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
-        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
         /// <returns>Torrents info</returns>
-        TransmissionTorrents? TorrentGet(string[] fields, bool asObjects = false, params long[] ids);
+        TransmissionTorrents? TorrentGet(string[] fields, params long[] ids);
 
         /// <summary>
         /// Move torrents to bottom in queue  (API: queue-move-bottom)
diff --git a/Transmission.API.RPC/ITransmissionClientAsync.cs b/Transmission.API.RPC/ITransmissionClientAsync.cs
index e0f0cda..6b51880 100644
--- a/Transmission.API.RPC/ITransmissionClientAsync.cs
+++ b/Transmission.API.RPC/ITransmissionClientAsync.cs
@@ -70,18 +70,16 @@ public interface ITransmissionClientAsync
         /// Get fields of recently active torrents (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
-        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <returns>Torrents info</returns>
-        Task<TransmissionTorrents?> TorrentGetRecentlyActiveAsync(string[] fields, bool asObjects = false);
+        Task<TransmissionTorrents?> TorrentGetRecentlyActiveAsync(string[] fields);
 
         /// <summary>
         /// Get fields of torrents from ids (API: torrent-get)
         /// </summary>
         /// <param name="fields">Fields of torrents</param>
-        /// <param name="asObjects">Whether to request the json as objects. Recommended to leave this set to false to use tables, which is slightly more performant.</param>
         /// <param name="ids">IDs of torrents (null or empty for get all torrents)</param>
         /// <returns>Torrents info</returns>
-        Task<TransmissionTorrents?> TorrentGetAsync(string[] fields, bool asObjects = false, params long[] ids);
+        Task<TransmissionTorrents?> TorrentGetAsync(string[] fields, params long[] ids);
 
         /// <summary>
         /// Move torrents to bottom in queue  (API: queue-move-bottom)