diff --git a/.travis.yml b/.travis.yml
index b9f3f025a..c175aae9c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,25 +3,26 @@ language: cpp
 os:
   - linux
   - osx
-osx_image: xcode7.3
 
 env:
   matrix:
     # Uncomment when Travis upgraded "Ubuntu 12.04 LTS" to a newer version whose repo will have a more up-to-date libtorrent package
     #- lt_branch=dist gui=true
     #- lt_branch=dist gui=false
-    - lt_branch=RC_1_0 gui=true build_system=cmake
-    - lt_branch=RC_1_0 gui=false build_system=cmake
-    - lt_branch=RC_1_0 gui=true build_system=qmake
-    - lt_branch=RC_1_0 gui=false build_system=qmake
+    - lt_branch=master gui=true build_system=cmake
+    - lt_branch=master gui=false build_system=cmake
+    - lt_branch=RC_1_1 gui=true build_system=cmake
+    - lt_branch=RC_1_1 gui=false build_system=cmake
+    - lt_branch=RC_1_1 gui=true build_system=qmake
+    - lt_branch=RC_1_1 gui=false build_system=qmake
   global:
     - secure: "OI9CUjj4lTb0HwwIZU5PbECU3hLlAL6KC8KsbwohG8/O3j5fLcnmDsK4Ad9us5cC39sS11Jcd1kDP2qRcCuST/glVNhLkcjKkiQerOfd5nQ/qL4JYfz/1mfP5mdpz9jHKzpLUIG+TXkbSTjP6VVmsb5KPT+3pKEdRFZB+Pu9+J8="
     - coverity_branch: coverity_scan
 
 matrix:
   allow_failures:
-    - env: lt_branch=RC_1_0 gui=true build_system=cmake
-    - env: lt_branch=RC_1_0 gui=false build_system=cmake
+    - env: lt_branch=RC_1_1 gui=true build_system=cmake
+    - env: lt_branch=RC_1_1 gui=false build_system=cmake
 
 branches:
   except:
@@ -59,16 +60,22 @@ addons:
       #- boost-latest
       - sourceline: 'ppa:qbittorrent-team/qbittorrent-stable'
       - sourceline: 'ppa:beineri/opt-qt551-trusty'
-      - sourceline: 'ppa:adrozdoff/cmake'
+      - sourceline: 'ppa:dluxen/cmake-backports'
+      - sourceline: 'ppa:rakhimov/boost'
     packages:
       # packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
       - [autoconf, automake, colormake]
-      - [cmake, ninja-build]
+      - [cmake3, ninja-build]
       - libssl-dev
-      - [libboost-dev, libboost-system-dev]
-      - libtorrent-rasterbar-dev
+      - [libboost1.61-dev, libboost-system1.61-dev, libboost-chrono1.61-dev, libboost-random1.61-dev]
       - [qt55base, qt55svg, qt55tools]
       - [gcc-6, g++-6]
+  homebrew:
+    update: true
+    packages:
+    - [colormake, ccache, zlib, qt]
+    - [boost, openssl]
+    - [cmake, ninja]
 
 before_install:
   # only allow specific build for coverity scan, others will stop
@@ -94,12 +101,33 @@ before_install:
       # Qt 5
       PATH=/opt/qt55/bin:${PATH}
 
-      if [ "$build_system" = "cmake" ]; then
-          COMPILER_VERSION=6
-          export CXX="${CXX}-${COMPILER_VERSION}" CC="${CC}-${COMPILER_VERSION}"
+      COMPILER_VERSION=6
+      export CXX="${CXX}-${COMPILER_VERSION}" CC="${CC}-${COMPILER_VERSION}"
+
+      # There is an outdated cmake in /usr/local/cmake-<version> which we don't want to use
+      # but /usr/local/cmake-<version>/bin is in $PATH, therefore
+      # remove all paths containing 'cmake' to use the executable from /usr/bin
+      export PATH=`echo ${PATH} | awk -v RS=: -v ORS=: '/cmake/ {next} {print}'`
+    fi
+  - |
+      if [ "$TRAVIS_OS_NAME" = "osx" ]; then
+        MY_CMAKE_OPENSSL_HINT="-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl/"
       fi
+  - |
+      cd "$TRAVIS_BUILD_DIR"
+      mkdir libtorrent
+      pushd libtorrent
+      git clone --depth 1 --branch ${lt_branch} https://github.com/arvidn/libtorrent.git .
+      mkdir -p cbuild/debug
+      pushd cbuild/debug
+      cmake -DCMAKE_BUILD_TYPE=Debug -Ddeprecated-functions=OFF "${MY_CMAKE_OPENSSL_HINT}" -G Ninja ../..
+      cmake --build . && sudo cmake --build . --target install
+      popd
+      popd
+  - |
+    if [ "$TRAVIS_OS_NAME" = "linux" ]; then
+      pkg-config --cflags --libs libtorrent-rasterbar
     fi
-
   # print settings
   - echo $lt_branch
   - echo $gui
@@ -119,45 +147,14 @@ install:
   - |
     if [ "$TRAVIS_OS_NAME" = "osx" ]; then
       # dependencies
-      brew update > /dev/null
       brew outdated "pkg-config" || brew upgrade "pkg-config"
-      brew install colormake ccache zlib qt
       PATH="/usr/local/opt/ccache/libexec:$PATH"
       brew link --force zlib qt
 
-      wget https://builds.shiki.hu/homebrew/version
-      if ! cmp --quiet "version" "$HOME/hombebrew_cache/version" ; then
-        echo "Cached files are different from server. Downloading new ones."
-        # First delete old files
-        rm -r "$HOME/hombebrew_cache"
-        mkdir "$HOME/hombebrew_cache"
-        cp "version" $HOME/hombebrew_cache
-        cd "$HOME/hombebrew_cache"
-        wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb
-        wget https://builds.shiki.hu/homebrew/5146d2df7e48f321511273fb9829ebdc3a6dc519f9ef36a344a343ae524c3ae6--libtorrent-rasterbar-1.1.9+git20180812.0bcf6cef23+patched-configure.el_capitan.bottle.tar.gz
-      fi
-
-      # Copy custom libtorrent bottle to homebrew's download cache so it can find and install it
-      # Also install our custom libtorrent formula by passing the local path to it
-      # These 2 files are restored from Travis' cache.
-      cp "$HOME/hombebrew_cache/5146d2df7e48f321511273fb9829ebdc3a6dc519f9ef36a344a343ae524c3ae6--libtorrent-rasterbar-1.1.9+git20180812.0bcf6cef23+patched-configure.el_capitan.bottle.tar.gz" "$(brew --cache)/downloads"
-      brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb"
-      
-      # NOTE about the bottle name
-      # The part before the "--" characters is a sha256 hash of the string
-      # of the URL homebrew itself would use to download the bottle.
-      # In this case the URL is the following:
-      # http://127.0.0.1/libtorrent-rasterbar-1.1.9+git20180812.0bcf6cef23+patched-configure.el_capitan.bottle.tar.gz
-
       if [ "$build_system" = "cmake" ]; then
-        brew outdated cmake || brew upgrade cmake
-        brew install ninja
-
-        ln -s /usr/local/opt/qt/mkspecs /usr/local/mkspecs
-        ln -s /usr/local/opt/qt/plugins /usr/local/plugins
+        sudo ln -s /usr/local/opt/qt/mkspecs /usr/local/mkspecs
+        sudo ln -s /usr/local/opt/qt/plugins /usr/local/plugins
       fi
-
-      MY_CMAKE_OPENSSL_HINT="-DOPENSSL_ROOT_DIR=/usr/local/opt/openssl/"
     fi
   - |
     if [ "$TRAVIS_BRANCH" != "$coverity_branch" ]; then
@@ -175,7 +172,10 @@ script:
       if [ "$gui" = "false" ]; then
         DISABLE_GUI_OPTION="-DCMAKE_DISABLE_FIND_PACKAGE_Qt5Widgets=ON"
       fi
+      # cmake in libtorrent RC_1_1 generates broken pkg-config file
+      [ "$lt_branch" == "RC_1_1" ] && MY_CMAKE_ARGS="${MY_CMAKE_ARGS} -DLibtorrentRasterbar_CUSTOM_BOOST_DEPENDENCIES=system"
       cmake $DISABLE_GUI_OPTION -DCMAKE_INSTALL_PREFIX="$qbt_path" "$MY_CMAKE_OPENSSL_HINT" \
+        ${MY_CMAKE_ARGS} \
         -G "Ninja" -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE ..
       BUILD_TOOL="ninja"
     fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fc3fc7f27..091d95317 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.11 FATAL_ERROR)
 
 message(AUTHOR_WARNING "If the build fails, please try the autotools/qmake method.")
 
@@ -30,7 +30,7 @@ include(GNUInstallDirs)
 include(FeatureSummary)
 
 # version requirements
-set(requiredBoostVersion 1.35)
+set(requiredBoostVersion 1.55)
 set(requiredQtVersion 5.5.1)
 
 if(WIN32)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c72229e1f..08a6fea00 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,11 @@
 set(CMAKE_CXX_STANDARD_REQUIRED True)
-set(CMAKE_CXX_STANDARD "11")
+if (cxx_std_14 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+    set(CMAKE_CXX_STANDARD 14)
+    message(STATUS "Building in C++14 mode")
+else()
+    set(CMAKE_CXX_STANDARD 11)
+    message(STATUS "Building in C++11 mode")
+endif()
 
 include(MacroQbtCompilerSettings)
 qbt_set_compiler_options()
diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt
index 4049d0cdf..32d9eb2fe 100644
--- a/src/base/CMakeLists.txt
+++ b/src/base/CMakeLists.txt
@@ -9,6 +9,7 @@ bittorrent/magneturi.h
 bittorrent/peerinfo.h
 bittorrent/private/bandwidthscheduler.h
 bittorrent/private/filterparserthread.h
+bittorrent/private/index.h
 bittorrent/private/resumedatasavingmanager.h
 bittorrent/private/speedmonitor.h
 bittorrent/private/statistics.h
diff --git a/src/base/bittorrent/infohash.cpp b/src/base/bittorrent/infohash.cpp
index c29c8cd85..084726866 100644
--- a/src/base/bittorrent/infohash.cpp
+++ b/src/base/bittorrent/infohash.cpp
@@ -30,6 +30,12 @@
 
 #include <QHash>
 
+#include <libtorrent/version.hpp>
+#if LIBTORRENT_VERSION_NUM >= 10200
+#include <boost/lexical_cast.hpp>
+#include "base/utils/string.h"
+#endif
+
 using namespace BitTorrent;
 
 InfoHash::InfoHash()
@@ -41,9 +47,13 @@ InfoHash::InfoHash(const libtorrent::sha1_hash &nativeHash)
     : m_valid(true)
     , m_nativeHash(nativeHash)
 {
+#if LIBTORRENT_VERSION_NUM < 10200
     char out[(libtorrent::sha1_hash::size * 2) + 1];
     libtorrent::to_hex(reinterpret_cast<const char*>(&m_nativeHash[0]), libtorrent::sha1_hash::size, out);
     m_hashString = QString(out);
+#else
+    m_hashString = QString::fromStdString(boost::lexical_cast<std::string>(m_nativeHash));
+#endif
 }
 
 InfoHash::InfoHash(const QString &hashString)
@@ -52,7 +62,11 @@ InfoHash::InfoHash(const QString &hashString)
 {
     QByteArray raw = m_hashString.toLatin1();
     if (raw.size() == 40)
+#if LIBTORRENT_VERSION_NUM < 10200
         m_valid = libtorrent::from_hex(raw.constData(), 40, reinterpret_cast<char*>(&m_nativeHash[0]));
+#else
+        m_valid = boost::conversion::try_lexical_convert(raw.constData(), m_nativeHash);
+#endif
 }
 
 
diff --git a/src/base/bittorrent/peerinfo.cpp b/src/base/bittorrent/peerinfo.cpp
index 6d5deb46a..33dc87b4d 100644
--- a/src/base/bittorrent/peerinfo.cpp
+++ b/src/base/bittorrent/peerinfo.cpp
@@ -29,6 +29,7 @@
 #include "peerinfo.h"
 
 #include "base/bittorrent/torrenthandle.h"
+#include "base/bittorrent/private/index.h"
 #include "base/net/geoipmanager.h"
 #include "base/unicodestrings.h"
 #include "base/utils/string.h"
@@ -60,17 +61,17 @@ PeerInfo::PeerInfo(const TorrentHandle *torrent, const libt::peer_info &nativeIn
 
 bool PeerInfo::fromDHT() const
 {
-    return (m_nativeInfo.source & libt::peer_info::dht);
+    return static_cast<bool>(m_nativeInfo.source & libt::peer_info::dht);
 }
 
 bool PeerInfo::fromPeX() const
 {
-    return (m_nativeInfo.source & libt::peer_info::pex);
+    return static_cast<bool>(m_nativeInfo.source & libt::peer_info::pex);
 }
 
 bool PeerInfo::fromLSD() const
 {
-    return (m_nativeInfo.source & libt::peer_info::lsd);
+    return static_cast<bool>(m_nativeInfo.source & libt::peer_info::lsd);
 }
 
 #ifndef DISABLE_COUNTRIES_RESOLUTION
@@ -82,102 +83,102 @@ QString PeerInfo::country() const
 
 bool PeerInfo::isInteresting() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::interesting);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::interesting);
 }
 
 bool PeerInfo::isChocked() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::choked);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::choked);
 }
 
 bool PeerInfo::isRemoteInterested() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::remote_interested);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::remote_interested);
 }
 
 bool PeerInfo::isRemoteChocked() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::remote_choked);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::remote_choked);
 }
 
 bool PeerInfo::isSupportsExtensions() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::supports_extensions);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::supports_extensions);
 }
 
 bool PeerInfo::isLocalConnection() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::local_connection);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::local_connection);
 }
 
 bool PeerInfo::isHandshake() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::handshake);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::handshake);
 }
 
 bool PeerInfo::isConnecting() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::connecting);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::connecting);
 }
 
 bool PeerInfo::isOnParole() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::on_parole);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::on_parole);
 }
 
 bool PeerInfo::isSeed() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::seed);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::seed);
 }
 
 bool PeerInfo::optimisticUnchoke() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::optimistic_unchoke);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::optimistic_unchoke);
 }
 
 bool PeerInfo::isSnubbed() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::snubbed);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::snubbed);
 }
 
 bool PeerInfo::isUploadOnly() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::upload_only);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::upload_only);
 }
 
 bool PeerInfo::isEndgameMode() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::endgame_mode);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::endgame_mode);
 }
 
 bool PeerInfo::isHolepunched() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::holepunched);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::holepunched);
 }
 
 bool PeerInfo::useI2PSocket() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::i2p_socket);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::i2p_socket);
 }
 
 bool PeerInfo::useUTPSocket() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::utp_socket);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::utp_socket);
 }
 
 bool PeerInfo::useSSLSocket() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::ssl_socket);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::ssl_socket);
 }
 
 bool PeerInfo::isRC4Encrypted() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::rc4_encrypted);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::rc4_encrypted);
 }
 
 bool PeerInfo::isPlaintextEncrypted() const
 {
-    return (m_nativeInfo.flags & libt::peer_info::plaintext_encrypted);
+    return static_cast<bool>(m_nativeInfo.flags & libt::peer_info::plaintext_encrypted);
 }
 
 PeerAddress PeerInfo::address() const
@@ -221,7 +222,7 @@ QBitArray PeerInfo::pieces() const
     QBitArray result(m_nativeInfo.pieces.size());
 
     for (int i = 0; i < m_nativeInfo.pieces.size(); ++i)
-        result.setBit(i, m_nativeInfo.pieces.get_bit(i));
+        result.setBit(i, m_nativeInfo.pieces.get_bit(makePieceIndex(i)));
 
     return result;
 }
@@ -397,5 +398,5 @@ QString PeerInfo::flagsDescription() const
 
 int PeerInfo::downloadingPieceIndex() const
 {
-    return m_nativeInfo.downloading_piece_index;
+    return indexValue(m_nativeInfo.downloading_piece_index);
 }
diff --git a/src/base/bittorrent/private/index.h b/src/base/bittorrent/private/index.h
new file mode 100644
index 000000000..5252408ee
--- /dev/null
+++ b/src/base/bittorrent/private/index.h
@@ -0,0 +1,84 @@
+/*
+ * Bittorrent Client using Qt and libtorrent.
+ * Copyright (C) 2018  Eugene Shalygin <eugene.shalygin@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * In addition, as a special exception, the copyright holders give permission to
+ * link this program with the OpenSSL project's "OpenSSL" library (or with
+ * modified versions of it that use the same license as the "OpenSSL" library),
+ * and distribute the linked executables. You must obey the GNU General Public
+ * License in all respects for all of the code used other than "OpenSSL".  If you
+ * modify file(s), you may extend this exception to your version of the file(s),
+ * but you are not obligated to do so. If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+#pragma once
+
+#include <libtorrent/version.hpp>
+#if LIBTORRENT_VERSION_NUM >= 10200
+#include <libtorrent/units.hpp>
+#endif
+
+namespace BitTorrent
+{
+#if LIBTORRENT_VERSION_NUM >= 10200
+
+    // these typedefs are needed until we drop C++11
+    using libt_file_index_t = libtorrent::file_index_t;
+    using libt_piece_index_t = libtorrent::piece_index_t;
+
+    inline constexpr libtorrent::file_index_t makeFileIndex(libtorrent::file_index_t::underlying_type index)
+    {
+        return libtorrent::file_index_t {index};
+    }
+
+    inline constexpr libtorrent::piece_index_t makePieceIndex(libtorrent::piece_index_t::underlying_type index)
+    {
+        return libtorrent::piece_index_t {index};
+    }
+
+    inline constexpr libtorrent::file_index_t::underlying_type indexValue(libtorrent::file_index_t index)
+    {
+        return static_cast<libtorrent::file_index_t::underlying_type>(index);
+    }
+
+    inline constexpr libtorrent::piece_index_t::underlying_type indexValue(libtorrent::piece_index_t index)
+    {
+        return static_cast<libtorrent::piece_index_t::underlying_type>(index);
+    }
+#else
+
+    using libt_file_index_t = int;
+    using libt_piece_index_t = int;
+
+    inline constexpr int makeFileIndex(int index)
+    {
+        return index;
+    }
+
+    inline constexpr int makePieceIndex(int index)
+    {
+        return index;
+    }
+
+    inline constexpr int indexValue(int index)
+    {
+        return index;
+    }
+#endif
+}
+
diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp
index fe27f6ef5..0d8cbeb10 100644
--- a/src/base/bittorrent/session.cpp
+++ b/src/base/bittorrent/session.cpp
@@ -60,6 +60,10 @@
 #include <libtorrent/session.hpp>
 #include <libtorrent/session_status.hpp>
 #include <libtorrent/torrent_info.hpp>
+#if LIBTORRENT_VERSION_NUM >= 10200
+#include <libtorrent/read_resume_data.hpp>
+#include <libtorrent/string_view.hpp>
+#endif
 
 #if LIBTORRENT_VERSION_NUM < 10100
 #include <libtorrent/lazy_entry.hpp>
@@ -92,6 +96,8 @@
 #include "tracker.h"
 #include "trackerentry.h"
 
+#include <boost/numeric/conversion/cast.hpp>
+
 #ifdef Q_OS_WIN
 #include <wincrypt.h>
 #include <iphlpapi.h>
@@ -138,13 +144,27 @@ namespace
         return result;
     }
 
+#if LIBTORRENT_VERSION_NUM < 10200
+    // we declare this shortcut here, because...
+    inline QString fromLibtorrentString(const std::string& str)
+    {
+        return QString::fromStdString(str);
+    }
+#else
+    // ... if libtorrent uses string_view, we need the other overload only in this file.
+    inline QString fromLibtorrentString(libtorrent::string_view str)
+    {
+        return QString::fromUtf8(str.data(), str.size());
+    }
+#endif
+
     template <typename Entry>
     QSet<QString> entryListToSetImpl(const Entry &entry)
     {
         Q_ASSERT(entry.type() == Entry::list_t);
         QSet<QString> output;
         for (int i = 0; i < entry.list_size(); ++i) {
-            const QString tag = QString::fromStdString(entry.list_string_value_at(i));
+            const QString tag = fromLibtorrentString(entry.list_string_value_at(i));
             if (Session::isValidTag(tag))
                 output.insert(tag);
             else
@@ -385,7 +405,7 @@ Session::Session(QObject *parent)
     connect(m_seedingLimitTimer, &QTimer::timeout, this, &Session::processShareLimits);
 
     // Set severity level of libtorrent session
-    const int alertMask = libt::alert::error_notification
+    const auto alertMask = libt::alert::error_notification
                     | libt::alert::peer_notification
                     | libt::alert::port_mapping_notification
                     | libt::alert::storage_notification
@@ -440,20 +460,24 @@ Session::Session(QObject *parent)
     pack.set_bool(libt::settings_pack::listen_system_port_fallback, false);
     pack.set_str(libt::settings_pack::user_agent, USER_AGENT);
     pack.set_bool(libt::settings_pack::use_dht_as_fallback, false);
+#if LIBTORRENT_VERSION_NUM < 10200
     // Disable support for SSL torrents for now
     pack.set_int(libt::settings_pack::ssl_listen, 0);
     // To prevent ISPs from blocking seeding
     pack.set_bool(libt::settings_pack::lazy_bitfields, true);
+#endif
     // Speed up exit
     pack.set_int(libt::settings_pack::stop_tracker_timeout, 1);
     pack.set_int(libt::settings_pack::auto_scrape_interval, 1200); // 20 minutes
     pack.set_int(libt::settings_pack::auto_scrape_min_interval, 900); // 15 minutes
     pack.set_int(libt::settings_pack::connection_speed, 20); // default is 10
     pack.set_bool(libt::settings_pack::no_connect_privileged_ports, false);
+#if LIBTORRENT_VERSION_NUM < 10200
     // Disk cache pool is rarely tested in libtorrent and doesn't free buffers
     // Soon to be deprecated there
     // More info: https://github.com/arvidn/libtorrent/issues/2251
     pack.set_bool(libt::settings_pack::use_disk_cache_pool, false);
+#endif
     // libtorrent 1.1 enables UPnP & NAT-PMP by default
     // turn them off before `libt::session` ctor to avoid split second effects
     pack.set_bool(libt::settings_pack::enable_upnp, false);
@@ -461,7 +485,11 @@ Session::Session(QObject *parent)
     pack.set_bool(libt::settings_pack::upnp_ignore_nonrouters, true);
     configure(pack);
 
+#if LIBTORRENT_VERSION_NUM < 10200
     m_nativeSession = new libt::session(pack, 0);
+#else
+    m_nativeSession = new libt::session(pack, libt::session::add_default_plugins);
+#endif
     m_nativeSession->set_alert_notify([this]()
     {
         QMetaObject::invokeMethod(this, "readAlerts", Qt::QueuedConnection);
@@ -966,18 +994,18 @@ void Session::setGlobalMaxRatio(qreal ratio)
     }
 }
 
-int Session::globalMaxSeedingMinutes() const
+std::chrono::minutes Session::globalMaxSeedingTime() const
 {
-    return m_globalMaxSeedingMinutes;
+    return std::chrono::minutes(m_globalMaxSeedingMinutes);
 }
 
-void Session::setGlobalMaxSeedingMinutes(int minutes)
+void Session::setGlobalMaxSeedingTime(std::chrono::minutes minutes)
 {
-    if (minutes < 0)
-        minutes = -1;
+    if (minutes < minutes.zero())
+        minutes = TorrentHandle::NO_SEEDING_TIME_LIMIT;
 
-    if (minutes != globalMaxSeedingMinutes()) {
-        m_globalMaxSeedingMinutes = minutes;
+    if (minutes != globalMaxSeedingTime()) {
+        m_globalMaxSeedingMinutes = minutes.count();
         updateSeedingLimitTimer();
     }
 }
@@ -1310,7 +1338,9 @@ void Session::configure(libtorrent::settings_pack &settingsPack)
 
         m_useProxy = (proxyConfig.type != Net::ProxyType::None);
     }
+#if LIBTORRENT_VERSION_NUM < 10200
     settingsPack.set_bool(libt::settings_pack::force_proxy, m_useProxy ? isForceProxyEnabled() : false);
+#endif
 
     settingsPack.set_bool(libt::settings_pack::announce_to_all_trackers, announceToAllTrackers());
     settingsPack.set_bool(libt::settings_pack::announce_to_all_tiers, announceToAllTiers());
@@ -1326,7 +1356,9 @@ void Session::configure(libtorrent::settings_pack &settingsPack)
                                                               : libt::settings_pack::disable_os_cache;
     settingsPack.set_int(libt::settings_pack::disk_io_read_mode, mode);
     settingsPack.set_int(libt::settings_pack::disk_io_write_mode, mode);
+#if LIBTORRENT_VERSION_NUM < 10200
     settingsPack.set_bool(libt::settings_pack::guided_read_cache, isGuidedReadCacheEnabled());
+#endif
 
     settingsPack.set_bool(libt::settings_pack::coalesce_reads, isCoalesceReadWriteEnabled());
     settingsPack.set_bool(libt::settings_pack::coalesce_writes, isCoalesceReadWriteEnabled());
@@ -1450,7 +1482,7 @@ void Session::configurePeerClasses()
     // Proactively do the same for 0.0.0.0 and address_v4::any()
     f.add_rule(libt::address_v4::any()
                , libt::address_v4::broadcast()
-               , 1 << libt::session::global_peer_class_id);
+               , 1 << static_cast<std::uint32_t>(libt::session::global_peer_class_id));
 #if TORRENT_USE_IPV6
     // IPv6 may not be available on OS and the parsing
     // would result in an exception -> abnormal program termination
@@ -1458,7 +1490,7 @@ void Session::configurePeerClasses()
     try {
         f.add_rule(libt::address_v6::from_string("::0")
                    , libt::address_v6::from_string("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
-                   , 1 << libt::session::global_peer_class_id);
+                   , 1 << static_cast<std::uint32_t>(libt::session::global_peer_class_id));
     }
     catch (std::exception &) {}
 #endif
@@ -1466,21 +1498,21 @@ void Session::configurePeerClasses()
         // local networks
         f.add_rule(libt::address_v4::from_string("10.0.0.0")
                    , libt::address_v4::from_string("10.255.255.255")
-                   , 1 << libt::session::local_peer_class_id);
+                   , 1 << static_cast<std::uint32_t>(libt::session::local_peer_class_id));
         f.add_rule(libt::address_v4::from_string("172.16.0.0")
                    , libt::address_v4::from_string("172.31.255.255")
-                   , 1 << libt::session::local_peer_class_id);
+                   , 1 << static_cast<std::uint32_t>(libt::session::local_peer_class_id));
         f.add_rule(libt::address_v4::from_string("192.168.0.0")
                    , libt::address_v4::from_string("192.168.255.255")
-                   , 1 << libt::session::local_peer_class_id);
+                   , 1 << static_cast<std::uint32_t>(libt::session::local_peer_class_id));
         // link local
         f.add_rule(libt::address_v4::from_string("169.254.0.0")
                    , libt::address_v4::from_string("169.254.255.255")
-                   , 1 << libt::session::local_peer_class_id);
+                   , 1 << static_cast<std::uint32_t>(libt::session::local_peer_class_id));
         // loopback
         f.add_rule(libt::address_v4::from_string("127.0.0.0")
                    , libt::address_v4::from_string("127.255.255.255")
-                   , 1 << libt::session::local_peer_class_id);
+                   , 1 << static_cast<std::uint32_t>(libt::session::local_peer_class_id));
 #if TORRENT_USE_IPV6
         // IPv6 may not be available on OS and the parsing
         // would result in an exception -> abnormal program termination
@@ -1489,15 +1521,15 @@ void Session::configurePeerClasses()
             // link local
             f.add_rule(libt::address_v6::from_string("fe80::")
                        , libt::address_v6::from_string("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
-                       , 1 << libt::session::local_peer_class_id);
+                       , 1 << static_cast<std::uint32_t>(libt::session::local_peer_class_id));
             // unique local addresses
             f.add_rule(libt::address_v6::from_string("fc00::")
                        , libt::address_v6::from_string("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
-                       , 1 << libt::session::local_peer_class_id);
+                       , 1 << static_cast<std::uint32_t>(libt::session::local_peer_class_id));
             // loopback
             f.add_rule(libt::address_v6::from_string("::1")
                        , libt::address_v6::from_string("::1")
-                       , 1 << libt::session::local_peer_class_id);
+                       , 1 << static_cast<std::uint32_t>(libt::session::local_peer_class_id));
         }
         catch (std::exception &) {}
 #endif
@@ -1808,16 +1840,16 @@ void Session::processShareLimits()
             }
 
             if (torrent->seedingTimeLimit() != TorrentHandle::NO_SEEDING_TIME_LIMIT) {
-                const int seedingTimeInMinutes = torrent->seedingTime() / 60;
-                int seedingTimeLimit = torrent->seedingTimeLimit();
+                const std::chrono::minutes seedingTime = std::chrono::duration_cast<std::chrono::minutes>(torrent->seedingTime());
+                std::chrono::minutes seedingTimeLimit = torrent->seedingTimeLimit();
                 if (seedingTimeLimit == TorrentHandle::USE_GLOBAL_SEEDING_TIME)
                      // If Global Seeding Time Limit is really set...
-                    seedingTimeLimit = globalMaxSeedingMinutes();
+                    seedingTimeLimit = globalMaxSeedingTime();
 
-                if (seedingTimeLimit >= 0) {
-                    qDebug("Seeding Time: %d (limit: %d)", seedingTimeInMinutes, seedingTimeLimit);
+                if (seedingTimeLimit >= seedingTimeLimit.zero()) {
+                    qDebug("Seeding Time: %ld (limit: %ld)", seedingTime.count(), seedingTimeLimit.count());
 
-                    if ((seedingTimeInMinutes <= TorrentHandle::MAX_SEEDING_TIME) && (seedingTimeInMinutes >= seedingTimeLimit)) {
+                    if ((seedingTime <= TorrentHandle::MAX_SEEDING_TIME) && (seedingTime >= seedingTimeLimit)) {
                         Logger *const logger = Logger::instance();
                         if (m_maxRatioAction == Remove) {
                             logger->addMessage(tr("'%1' reached the maximum seeding time you set. Removed.").arg(torrent->name()));
@@ -1962,8 +1994,7 @@ bool Session::cancelLoadMetadata(const InfoHash &hash)
     m_loadedMetadata.remove(hash);
     libt::torrent_handle torrent = m_nativeSession->find_torrent(hash);
     if (!torrent.is_valid()) return false;
-
-    if (!torrent.status(0).has_metadata) {
+    if (!torrent.status({}).has_metadata) {
         // if hidden torrent is still loading metadata...
         --m_extraLimit;
         adjustLimits();
@@ -2134,9 +2165,26 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
 
     // If empty then Automatic mode, otherwise Manual mode
     QString savePath = params.savePath.isEmpty() ? categorySavePath(params.category) : params.savePath;
+    const bool fromMagnetUri = magnetUri.isValid();
     libt::add_torrent_params p;
+    if (params.restored && !fromMagnetUri) {
+#if LIBTORRENT_VERSION_NUM >= 10200
+        libt::error_code ec;
+        p = libt::read_resume_data(fastresumeData, ec);
+#else
+        // Set torrent fast resume data
+        p.resume_data = {fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size()};
+        p.flags |= libt::add_torrent_params::flag_use_resume_save_path;
+#endif
+    }
+
     InfoHash hash;
-    const bool fromMagnetUri = magnetUri.isValid();
+
+#if LIBTORRENT_VERSION_NUM < 10200
+    std::vector<boost::uint8_t> filePriorities;
+#else
+    std::vector<libt::download_priority_t> filePriorities;
+#endif
 
     if (fromMagnetUri) {
         hash = magnetUri.hash();
@@ -2148,7 +2196,11 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
             --m_extraLimit;
 
             try {
+#if LIBTORRENT_VERSION_NUM < 10200
                 handle.auto_managed(false);
+#else
+                handle.unset_flags(libt::torrent_flags::auto_managed);
+#endif
                 handle.pause();
             }
             catch (std::exception &) {}
@@ -2194,13 +2246,10 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
         return false;
     }
 
-    if (params.restored && !fromMagnetUri) {
-        // Set torrent fast resume data
-        p.resume_data = {fastresumeData.constData(), fastresumeData.constData() + fastresumeData.size()};
-        p.flags |= libt::add_torrent_params::flag_use_resume_save_path;
-    }
-    else {
-        p.file_priorities = {params.filePriorities.begin(), params.filePriorities.end()};
+    if (!params.restored || fromMagnetUri) {
+        for (auto prio : params.filePriorities)
+            filePriorities.push_back(static_cast<decltype(filePriorities)::value_type>(prio));
+        p.file_priorities = filePriorities;
     }
 
     // We should not add torrent if it already
@@ -2224,7 +2273,7 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
         p.storage_mode = libt::storage_mode_allocate;
     else
         p.storage_mode = libt::storage_mode_sparse;
-
+#if LIBTORRENT_VERSION_NUM < 10200
     p.flags |= libt::add_torrent_params::flag_paused; // Start in pause
     p.flags &= ~libt::add_torrent_params::flag_auto_managed; // Because it is added in paused state
     p.flags &= ~libt::add_torrent_params::flag_duplicate_is_error; // Already checked
@@ -2235,6 +2284,18 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
         p.flags |= libt::add_torrent_params::flag_seed_mode;
     else
         p.flags &= ~libt::add_torrent_params::flag_seed_mode;
+#else
+    p.flags |= libt::torrent_flags::paused; // Start in pause
+    p.flags |= libt::torrent_flags::auto_managed; // Because it is added in paused state
+    p.flags &= ~libt::torrent_flags::duplicate_is_error; // Already checked
+
+    // Seeding mode
+    // Skip checking and directly start seeding (new in libtorrent v0.15)
+    if (params.skipChecking)
+        p.flags |= libt::torrent_flags::seed_mode;
+    else
+        p.flags &= ~libt::torrent_flags::seed_mode;
+#endif
 
     // Limits
     p.max_connections = maxConnectionsPerTorrent();
@@ -2314,10 +2375,19 @@ bool Session::loadMetadata(const MagnetUri &magnetUri)
     p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
 
     // Forced start
+#if LIBTORRENT_VERSION_NUM < 10200
     p.flags &= ~libt::add_torrent_params::flag_paused;
     p.flags &= ~libt::add_torrent_params::flag_auto_managed;
+#else
+    p.flags &= ~libt::torrent_flags::paused;
+    p.flags &= ~libt::torrent_flags::auto_managed;
+#endif
     // Solution to avoid accidental file writes
+#if LIBTORRENT_VERSION_NUM < 10200
     p.flags |= libt::add_torrent_params::flag_upload_mode;
+#else
+    p.flags &= ~libt::torrent_flags::upload_mode;
+#endif
 
     // Adding torrent to BitTorrent session
     libt::error_code ec;
@@ -3101,10 +3171,12 @@ void Session::setUseOSCache(bool use)
     }
 }
 
+#if LIBTORRENT_VERSION_NUM < 10200
 bool Session::isGuidedReadCacheEnabled() const
 {
     return m_guidedReadCacheEnabled;
 }
+#endif
 
 void Session::setGuidedReadCacheEnabled(bool enabled)
 {
@@ -3520,7 +3592,7 @@ bool Session::isKnownTorrent(const InfoHash &hash) const
 void Session::updateSeedingLimitTimer()
 {
     if ((globalMaxRatio() == TorrentHandle::NO_RATIO_LIMIT) && !hasPerTorrentRatioLimit()
-        && (globalMaxSeedingMinutes() == TorrentHandle::NO_SEEDING_TIME_LIMIT) && !hasPerTorrentSeedingTimeLimit()) {
+        && (globalMaxSeedingTime() == TorrentHandle::NO_SEEDING_TIME_LIMIT) && !hasPerTorrentSeedingTimeLimit()) {
         if (m_seedingLimitTimer->isActive())
             m_seedingLimitTimer->stop();
     }
@@ -3541,7 +3613,7 @@ void Session::handleTorrentsPrioritiesChanged()
     for (TorrentHandle *const torrent : torrents()) {
         if (!torrent->isSeed()) {
             // cached vs actual queue position, qBt starts queue at 1
-            if (torrent->queuePosition() != (torrent->nativeHandle().queue_position() + 1))
+            if (torrent->queuePosition() != (static_cast<int>(torrent->nativeHandle().queue_position()) + 1))
                 saveTorrentResumeData(torrent);
         }
     }
@@ -3758,7 +3830,7 @@ bool Session::hasPerTorrentRatioLimit() const
 bool Session::hasPerTorrentSeedingTimeLimit() const
 {
     foreach (TorrentHandle *const torrent, m_torrents)
-        if (torrent->seedingTimeLimit() >= 0) return true;
+        if (torrent->seedingTimeLimit() >= std::chrono::minutes::zero()) return true;
 
     return false;
 }
@@ -4165,7 +4237,7 @@ void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
         saveTorrentResumeData(torrent);
     }
 
-    if (((torrent->ratioLimit() >= 0) || (torrent->seedingTimeLimit() >= 0))
+    if (((torrent->ratioLimit() >= 0) || (torrent->seedingTimeLimit() >= std::chrono::minutes::zero()))
         && !m_seedingLimitTimer->isActive())
         m_seedingLimitTimer->start();
 
@@ -4271,7 +4343,11 @@ void Session::handlePortmapAlert(libt::portmap_alert *p)
 void Session::handlePeerBlockedAlert(libt::peer_blocked_alert *p)
 {
     boost::system::error_code ec;
-    std::string ip = p->ip.to_string(ec);
+#if LIBTORRENT_VERSION_NUM < 10200
+    const std::string ip = p->ip.to_string(ec);
+#else
+    const std::string ip = p->endpoint.address().to_string(ec);
+#endif
     QString reason;
     switch (p->reason) {
     case libt::peer_blocked_alert::ip_filter:
@@ -4298,10 +4374,70 @@ void Session::handlePeerBlockedAlert(libt::peer_blocked_alert *p)
         Logger::instance()->addPeer(QString::fromLatin1(ip.c_str()), true, reason);
 }
 
+namespace
+{
+    // helpers to handle differences in libtorrent 1.1 and 1.2 APIs
+    template <typename AlertType>
+    std::pair<libt::address, int> alertAddressAndPort(const AlertType* a)
+    {
+#if LIBTORRENT_VERSION_NUM < 10200
+        return {a->endpoint.address(), a->endpoint.port()};
+#else
+        return {a->address, a->port};
+#endif
+    }
+
+    template <>
+    std::pair<libt::address, int> alertAddressAndPort(const libt::peer_ban_alert* a)
+    {
+#if LIBTORRENT_VERSION_NUM < 10200
+        return {a->ip.address(), a->ip.port()};
+#else
+        return {a->endpoint.address(), a->endpoint.port()};
+#endif
+    }
+
+    template <typename AlertType>
+#if LIBTORRENT_VERSION_NUM >= 10200
+    auto alertSocketType(const AlertType *p) -> decltype(p->socket_type)
+#else
+    auto alertSocketType(const AlertType *p) -> decltype(p->sock_type)
+#endif
+    {
+#if LIBTORRENT_VERSION_NUM < 10200
+        return p->sock_type;
+#else
+        return p->socket_type;
+#endif
+    }
+
+    template <typename AlertType>
+    const char* alertSocketTypeName(const AlertType *p)
+    {
+        const auto socketType = alertSocketType(p);
+        switch (socketType) {
+            case decltype(socketType)::tcp:
+                return "TCP";
+            case decltype(socketType)::tcp_ssl:
+                return "TCP_SSL";
+            case decltype(socketType)::udp:
+                return "UDP";
+            case decltype(socketType)::i2p:
+                return "I2P";
+            case decltype(socketType)::socks5:
+                return "SOCKS5";
+            case decltype(socketType)::utp_ssl:
+                return "UTP_SSL";
+            default:
+                throw std::logic_error("Unexpected socket type");
+        }
+    }
+}
+
 void Session::handlePeerBanAlert(libt::peer_ban_alert *p)
 {
     boost::system::error_code ec;
-    std::string ip = p->ip.address().to_string(ec);
+    std::string ip = alertAddressAndPort(p).first.to_string(ec);
     if (!ec)
         Logger::instance()->addPeer(QString::fromLatin1(ip.c_str()), false);
 }
@@ -4310,27 +4446,21 @@ void Session::handleUrlSeedAlert(libt::url_seed_alert *p)
 {
     Logger::instance()->addMessage(tr("URL seed lookup failed for URL: '%1', message: %2")
 #if LIBTORRENT_VERSION_NUM >= 10100
-                                   .arg(QString::fromStdString(p->server_url()))
+                                    .arg(QString::fromStdString(p->server_url()))
 #else
-                                   .arg(QString::fromStdString(p->url))
+                                    .arg(QString::fromStdString(p->url))
 #endif
-                                   .arg(QString::fromStdString(p->message())), Log::CRITICAL);
+                                    .arg(QString::fromStdString(p->message())), Log::CRITICAL);
 }
 
 void Session::handleListenSucceededAlert(libt::listen_succeeded_alert *p)
 {
     boost::system::error_code ec;
-    QString proto = "TCP";
-    if (p->sock_type == libt::listen_succeeded_alert::udp)
-        proto = "UDP";
-    else if (p->sock_type == libt::listen_succeeded_alert::tcp)
-        proto = "TCP";
-    else if (p->sock_type == libt::listen_succeeded_alert::tcp_ssl)
-        proto = "TCP_SSL";
-    qDebug() << "Successfully listening on " << proto << p->endpoint.address().to_string(ec).c_str() << '/' << p->endpoint.port();
+    const QString proto = QString::fromLatin1(alertSocketTypeName(p));
+
     Logger::instance()->addMessage(
         tr("qBittorrent is successfully listening on interface %1 port: %2/%3", "e.g: qBittorrent is successfully listening on interface 192.168.0.1 port: TCP/6881")
-            .arg(p->endpoint.address().to_string(ec).c_str(), proto, QString::number(p->endpoint.port())), Log::INFO);
+            .arg(alertAddressAndPort(p).first.to_string(ec).c_str(), proto, QString::number(alertAddressAndPort(p).second)), Log::INFO);
 
     // Force reannounce on all torrents because some trackers blacklist some ports
     std::vector<libt::torrent_handle> torrents = m_nativeSession->get_torrents();
@@ -4343,22 +4473,12 @@ void Session::handleListenSucceededAlert(libt::listen_succeeded_alert *p)
 void Session::handleListenFailedAlert(libt::listen_failed_alert *p)
 {
     boost::system::error_code ec;
-    QString proto = "TCP";
-    if (p->sock_type == libt::listen_failed_alert::udp)
-        proto = "UDP";
-    else if (p->sock_type == libt::listen_failed_alert::tcp)
-        proto = "TCP";
-    else if (p->sock_type == libt::listen_failed_alert::tcp_ssl)
-        proto = "TCP_SSL";
-    else if (p->sock_type == libt::listen_failed_alert::i2p)
-        proto = "I2P";
-    else if (p->sock_type == libt::listen_failed_alert::socks5)
-        proto = "SOCKS5";
-    qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << '/' << p->endpoint.port();
+    const QString proto = QString::fromLatin1(alertSocketTypeName(p));
+
     Logger::instance()->addMessage(
         tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4.",
             "e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use.")
-        .arg(p->endpoint.address().to_string(ec).c_str(), proto, QString::number(p->endpoint.port())
+        .arg(alertAddressAndPort(p).first.to_string(ec).c_str(), proto, QString::number(alertAddressAndPort(p).second)
             , QString::fromLocal8Bit(p->error.message().c_str()))
         , Log::CRITICAL);
 }
@@ -4374,23 +4494,39 @@ void Session::handleSessionStatsAlert(libt::session_stats_alert *p)
 {
     qreal interval = m_statsUpdateTimer.restart() / 1000.;
 
-    m_status.hasIncomingConnections = static_cast<bool>(p->values[m_metricIndices.net.hasIncomingConnections]);
+#if LIBTORRENT_VERSION_NUM < 10200
+    const auto &counters = p->values;
+#else
+    const auto counters = p->counters();
+#endif
 
-    const auto ipOverheadDownload = p->values[m_metricIndices.net.recvIPOverheadBytes];
-    const auto ipOverheadUpload = p->values[m_metricIndices.net.sentIPOverheadBytes];
-    const auto totalDownload = p->values[m_metricIndices.net.recvBytes] + ipOverheadDownload;
-    const auto totalUpload = p->values[m_metricIndices.net.sentBytes] + ipOverheadUpload;
-    const auto totalPayloadDownload = p->values[m_metricIndices.net.recvPayloadBytes];
-    const auto totalPayloadUpload = p->values[m_metricIndices.net.sentPayloadBytes];
-    const auto trackerDownload = p->values[m_metricIndices.net.recvTrackerBytes];
-    const auto trackerUpload = p->values[m_metricIndices.net.sentTrackerBytes];
-    const auto dhtDownload = p->values[m_metricIndices.dht.dhtBytesIn];
-    const auto dhtUpload = p->values[m_metricIndices.dht.dhtBytesOut];
+    m_status.hasIncomingConnections = static_cast<bool>(counters[m_metricIndices.net.hasIncomingConnections]);
+
+    const auto ipOverheadDownload = counters[m_metricIndices.net.recvIPOverheadBytes];
+    const auto ipOverheadUpload = counters[m_metricIndices.net.sentIPOverheadBytes];
+    const auto totalDownload = counters[m_metricIndices.net.recvBytes] + ipOverheadDownload;
+    const auto totalUpload = counters[m_metricIndices.net.sentBytes] + ipOverheadUpload;
+    const auto totalPayloadDownload = counters[m_metricIndices.net.recvPayloadBytes];
+    const auto totalPayloadUpload = counters[m_metricIndices.net.sentPayloadBytes];
+    const auto trackerDownload = counters[m_metricIndices.net.recvTrackerBytes];
+    const auto trackerUpload = counters[m_metricIndices.net.sentTrackerBytes];
+    const auto dhtDownload = counters[m_metricIndices.dht.dhtBytesIn];
+    const auto dhtUpload = counters[m_metricIndices.dht.dhtBytesOut];
+
+#if __cplusplus < 201402L
+#if LIBTORRENT_VERSION_NUM >= 10200
+    using libt_counter_type = std::int64_t;
+#else
+    using libt_counter_type = boost::uint64_t;
+#endif
 
-    auto calcRate = [interval](quint64 previous, quint64 current)
+    auto calcRate = [interval](quint64 previous, libt_counter_type current)
+#else
+    auto calcRate = [interval](auto previous, auto current)
+#endif
     {
-        Q_ASSERT(current >= previous);
-        return static_cast<quint64>((current - previous) / interval);
+        Q_ASSERT(boost::numeric_cast<qint64>(current) >= boost::numeric_cast<qint64>(previous));
+        return static_cast<quint64>((boost::numeric_cast<quint64>(current) - previous) / interval);
     };
 
     m_status.payloadDownloadRate = calcRate(m_status.totalPayloadDownload, totalPayloadDownload);
@@ -4404,33 +4540,33 @@ void Session::handleSessionStatsAlert(libt::session_stats_alert *p)
     m_status.trackerDownloadRate = calcRate(m_status.trackerDownload, trackerDownload);
     m_status.trackerUploadRate = calcRate(m_status.trackerUpload, trackerUpload);
 
-    m_status.totalDownload = totalDownload;
-    m_status.totalUpload = totalUpload;
-    m_status.totalPayloadDownload = totalPayloadDownload;
-    m_status.totalPayloadUpload = totalPayloadUpload;
-    m_status.ipOverheadDownload = ipOverheadDownload;
-    m_status.ipOverheadUpload = ipOverheadUpload;
-    m_status.trackerDownload = trackerDownload;
-    m_status.trackerUpload = trackerUpload;
-    m_status.dhtDownload = dhtDownload;
-    m_status.dhtUpload = dhtUpload;
-    m_status.totalWasted = p->values[m_metricIndices.net.recvRedundantBytes]
-            + p->values[m_metricIndices.net.recvFailedBytes];
-    m_status.dhtNodes = p->values[m_metricIndices.dht.dhtNodes];
-    m_status.diskReadQueue = p->values[m_metricIndices.peer.numPeersUpDisk];
-    m_status.diskWriteQueue = p->values[m_metricIndices.peer.numPeersDownDisk];
-    m_status.peersCount = p->values[m_metricIndices.peer.numPeersConnected];
-
-    const int numBlocksRead = p->values[m_metricIndices.disk.numBlocksRead];
-    const int numBlocksCacheHits = p->values[m_metricIndices.disk.numBlocksCacheHits];
-    m_cacheStatus.totalUsedBuffers = p->values[m_metricIndices.disk.diskBlocksInUse];
+    m_status.totalDownload = boost::numeric_cast<quint64>(totalDownload);
+    m_status.totalUpload = boost::numeric_cast<quint64>(totalUpload);
+    m_status.totalPayloadDownload = boost::numeric_cast<quint64>(totalPayloadDownload);
+    m_status.totalPayloadUpload = boost::numeric_cast<quint64>(totalPayloadUpload);
+    m_status.ipOverheadDownload = boost::numeric_cast<quint64>(ipOverheadDownload);
+    m_status.ipOverheadUpload = boost::numeric_cast<quint64>(ipOverheadUpload);
+    m_status.trackerDownload = boost::numeric_cast<quint64>(trackerDownload);
+    m_status.trackerUpload = boost::numeric_cast<quint64>(trackerUpload);
+    m_status.dhtDownload = boost::numeric_cast<quint64>(dhtDownload);
+    m_status.dhtUpload = boost::numeric_cast<quint64>(dhtUpload);
+    m_status.totalWasted = boost::numeric_cast<quint64>(counters[m_metricIndices.net.recvRedundantBytes]
+            + counters[m_metricIndices.net.recvFailedBytes]);
+    m_status.dhtNodes = boost::numeric_cast<quint64>(counters[m_metricIndices.dht.dhtNodes]);
+    m_status.diskReadQueue = boost::numeric_cast<quint64>(counters[m_metricIndices.peer.numPeersUpDisk]);
+    m_status.diskWriteQueue = boost::numeric_cast<quint64>(counters[m_metricIndices.peer.numPeersDownDisk]);
+    m_status.peersCount = boost::numeric_cast<quint64>(counters[m_metricIndices.peer.numPeersConnected]);
+
+    const int numBlocksRead = counters[m_metricIndices.disk.numBlocksRead];
+    const int numBlocksCacheHits = counters[m_metricIndices.disk.numBlocksCacheHits];
+    m_cacheStatus.totalUsedBuffers = boost::numeric_cast<quint64>(counters[m_metricIndices.disk.diskBlocksInUse]);
     m_cacheStatus.readRatio = static_cast<qreal>(numBlocksCacheHits) / std::max(numBlocksCacheHits + numBlocksRead, 1);
-    m_cacheStatus.jobQueueLength = p->values[m_metricIndices.disk.queuedDiskJobs];
+    m_cacheStatus.jobQueueLength = boost::numeric_cast<quint64>(counters[m_metricIndices.disk.queuedDiskJobs]);
 
-    quint64 totalJobs = p->values[m_metricIndices.disk.writeJobs] + p->values[m_metricIndices.disk.readJobs]
-                  + p->values[m_metricIndices.disk.hashJobs];
+    quint64 totalJobs = boost::numeric_cast<quint64>(counters[m_metricIndices.disk.writeJobs] + counters[m_metricIndices.disk.readJobs]
+                    + counters[m_metricIndices.disk.hashJobs]);
     m_cacheStatus.averageJobTime = totalJobs > 0
-                                   ? (p->values[m_metricIndices.disk.diskJobTime] / totalJobs) : 0;
+                                    ? boost::numeric_cast<quint64>(counters[m_metricIndices.disk.diskJobTime]) / totalJobs : 0u;
 
     emit statsUpdated();
 }
@@ -4540,31 +4676,32 @@ namespace
 #endif
 
         torrentParams.savePath = Profile::instance().fromPortablePath(
-            Utils::Fs::fromNativePath(QString::fromStdString(fast.dict_find_string_value("qBt-savePath"))));
+            Utils::Fs::fromNativePath(fromLibtorrentString(fast.dict_find_string_value("qBt-savePath"))));
 
-        std::string ratioLimitString = fast.dict_find_string_value("qBt-ratioLimit");
+        const auto ratioLimitString = fast.dict_find_string_value("qBt-ratioLimit");
         if (ratioLimitString.empty())
             torrentParams.ratioLimit = fast.dict_find_int_value("qBt-ratioLimit", TorrentHandle::USE_GLOBAL_RATIO * 1000) / 1000.0;
         else
-            torrentParams.ratioLimit = QString::fromStdString(ratioLimitString).toDouble();
-        torrentParams.seedingTimeLimit = fast.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME);
+            torrentParams.ratioLimit = fromLibtorrentString(ratioLimitString).toDouble();
+        torrentParams.seedingTimeLimit =
+            std::chrono::minutes(fast.dict_find_int_value("qBt-seedingTimeLimit", TorrentHandle::USE_GLOBAL_SEEDING_TIME.count()));
         // **************************************************************************************
         // Workaround to convert legacy label to category
         // TODO: Should be removed in future
-        torrentParams.category = QString::fromStdString(fast.dict_find_string_value("qBt-label"));
+        torrentParams.category = fromLibtorrentString(fast.dict_find_string_value("qBt-label"));
         if (torrentParams.category.isEmpty())
         // **************************************************************************************
-            torrentParams.category = QString::fromStdString(fast.dict_find_string_value("qBt-category"));
+            torrentParams.category = fromLibtorrentString(fast.dict_find_string_value("qBt-category"));
         // auto because the return type depends on the #if above.
         const auto tagsEntry = fast.dict_find_list("qBt-tags");
         if (isList(tagsEntry))
             torrentParams.tags = entryListToSet(tagsEntry);
-        torrentParams.name = QString::fromStdString(fast.dict_find_string_value("qBt-name"));
+        torrentParams.name = fromLibtorrentString(fast.dict_find_string_value("qBt-name"));
         torrentParams.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus");
         torrentParams.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled");
         torrentParams.hasRootFolder = fast.dict_find_int_value("qBt-hasRootFolder");
 
-        magnetUri = MagnetUri(QString::fromStdString(fast.dict_find_string_value("qBt-magnetUri")));
+        magnetUri = MagnetUri(fromLibtorrentString(fast.dict_find_string_value("qBt-magnetUri")));
         torrentParams.paused = fast.dict_find_int_value("qBt-paused");
         torrentParams.forced = fast.dict_find_int_value("qBt-forced");
         torrentParams.firstLastPiecePriority = fast.dict_find_int_value("qBt-firstLastPiecePriority");
diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h
index c4cd2185f..95a4279e7 100644
--- a/src/base/bittorrent/session.h
+++ b/src/base/bittorrent/session.h
@@ -32,6 +32,7 @@
 
 #include <libtorrent/version.hpp>
 
+#include <chrono>
 #include <vector>
 
 #include <QFile>
@@ -59,6 +60,9 @@
 #include "sessionstatus.h"
 #include "torrentinfo.h"
 
+#if LIBTORRENT_VERSION_NUM >= 10108 // #if __has_include(<libtorrent/fwd.hpp>)
+#include <libtorrent/fwd.hpp>
+#else
 namespace libtorrent
 {
     class session;
@@ -108,6 +112,8 @@ namespace libtorrent
 #endif
 }
 
+#endif
+
 class QThread;
 class QTimer;
 class QStringList;
@@ -198,43 +204,43 @@ namespace BitTorrent
     {
         struct
         {
-            int hasIncomingConnections = 0;
-            int sentPayloadBytes = 0;
-            int recvPayloadBytes = 0;
-            int sentBytes = 0;
-            int recvBytes = 0;
-            int sentIPOverheadBytes = 0;
-            int recvIPOverheadBytes = 0;
-            int sentTrackerBytes = 0;
-            int recvTrackerBytes = 0;
-            int recvRedundantBytes = 0;
-            int recvFailedBytes = 0;
+            std::size_t hasIncomingConnections = 0;
+            std::size_t sentPayloadBytes = 0;
+            std::size_t recvPayloadBytes = 0;
+            std::size_t sentBytes = 0;
+            std::size_t recvBytes = 0;
+            std::size_t sentIPOverheadBytes = 0;
+            std::size_t recvIPOverheadBytes = 0;
+            std::size_t sentTrackerBytes = 0;
+            std::size_t recvTrackerBytes = 0;
+            std::size_t recvRedundantBytes = 0;
+            std::size_t recvFailedBytes = 0;
         } net;
 
         struct
         {
-            int numPeersConnected = 0;
-            int numPeersUpDisk = 0;
-            int numPeersDownDisk = 0;
+            std::size_t numPeersConnected = 0;
+            std::size_t numPeersUpDisk = 0;
+            std::size_t numPeersDownDisk = 0;
         } peer;
 
         struct
         {
-            int dhtBytesIn = 0;
-            int dhtBytesOut = 0;
-            int dhtNodes = 0;
+            std::size_t dhtBytesIn = 0;
+            std::size_t dhtBytesOut = 0;
+            std::size_t dhtNodes = 0;
         } dht;
 
         struct
         {
-            int diskBlocksInUse = 0;
-            int numBlocksRead = 0;
-            int numBlocksCacheHits = 0;
-            int writeJobs = 0;
-            int readJobs = 0;
-            int hashJobs = 0;
-            int queuedDiskJobs = 0;
-            int diskJobTime = 0;
+            std::size_t diskBlocksInUse = 0;
+            std::size_t numBlocksRead = 0;
+            std::size_t numBlocksCacheHits = 0;
+            std::size_t writeJobs = 0;
+            std::size_t readJobs = 0;
+            std::size_t hashJobs = 0;
+            std::size_t queuedDiskJobs = 0;
+            std::size_t diskJobTime = 0;
         } disk;
     };
 #endif
@@ -297,8 +303,8 @@ namespace BitTorrent
 
         qreal globalMaxRatio() const;
         void setGlobalMaxRatio(qreal ratio);
-        int globalMaxSeedingMinutes() const;
-        void setGlobalMaxSeedingMinutes(int minutes);
+        std::chrono::minutes globalMaxSeedingTime() const;
+        void setGlobalMaxSeedingTime(std::chrono::minutes minutes);
         bool isDHTEnabled() const;
         void setDHTEnabled(bool enabled);
         bool isLSDEnabled() const;
@@ -383,7 +389,9 @@ namespace BitTorrent
         void setDiskCacheTTL(int ttl);
         bool useOSCache() const;
         void setUseOSCache(bool use);
+#if LIBTORRENT_VERSION_NUM < 10200
         bool isGuidedReadCacheEnabled() const;
+#endif
         void setGuidedReadCacheEnabled(bool enabled);
         bool isCoalesceReadWriteEnabled() const;
         void setCoalesceReadWriteEnabled(bool enabled);
diff --git a/src/base/bittorrent/torrentcreatorthread.cpp b/src/base/bittorrent/torrentcreatorthread.cpp
index 30a05dee6..b84a3e8fe 100644
--- a/src/base/bittorrent/torrentcreatorthread.cpp
+++ b/src/base/bittorrent/torrentcreatorthread.cpp
@@ -42,6 +42,7 @@
 #include <QFileInfo>
 #include <QHash>
 
+#include "base/bittorrent/private/index.h"
 #include "base/global.h"
 #include "base/utils/fs.h"
 #include "base/utils/misc.h"
@@ -135,9 +136,12 @@ void TorrentCreatorThread::run()
 #if LIBTORRENT_VERSION_NUM < 10100
         libt::create_torrent newTorrent(fs, m_params.pieceSize, -1
             , (m_params.isAlignmentOptimized ? libt::create_torrent::optimize : 0));
-#else
+#elif LIBTORRENT_VERSION_NUM < 10200
         libt::create_torrent newTorrent(fs, m_params.pieceSize, -1
             , (m_params.isAlignmentOptimized ? libt::create_torrent::optimize_alignment : 0));
+#else
+        libt::create_torrent newTorrent(fs, m_params.pieceSize, -1
+            , (m_params.isAlignmentOptimized ? libt::create_torrent::optimize_alignment : libt::create_flags_t()));
 #endif
 
         // Add url seeds
@@ -159,7 +163,7 @@ void TorrentCreatorThread::run()
 
         // calculate the hash for all pieces
         libt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
-            , [this, &newTorrent](const int n) { sendProgressSignal(n, newTorrent.num_pieces()); });
+            , [this, &newTorrent](const BitTorrent::libt_piece_index_t n) { this->sendProgressSignal(indexValue(n), newTorrent.num_pieces()); });
         // Set qBittorrent as creator and add user comment to
         // torrent_info structure
         newTorrent.set_creator(creatorStr.toUtf8().constData());
@@ -212,8 +216,11 @@ int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const i
 #if LIBTORRENT_VERSION_NUM < 10100
     return libt::create_torrent(fs, pieceSize, -1
         , (isAlignmentOptimized ? libt::create_torrent::optimize : 0)).num_pieces();
-#else
+#elif LIBTORRENT_VERSION_NUM < 10200
     return libt::create_torrent(fs, pieceSize, -1
         , (isAlignmentOptimized ? libt::create_torrent::optimize_alignment : 0)).num_pieces();
+#else
+    return libt::create_torrent(fs, pieceSize, -1
+        , (isAlignmentOptimized ? libt::create_torrent::optimize_alignment : libt::create_flags_t())).num_pieces();
 #endif
 }
diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp
index da5520a33..d0dd064a8 100644
--- a/src/base/bittorrent/torrenthandle.cpp
+++ b/src/base/bittorrent/torrenthandle.cpp
@@ -48,8 +48,13 @@
 #if LIBTORRENT_VERSION_NUM >= 10100
 #include <libtorrent/time.hpp>
 #endif
+#if LIBTORRENT_VERSION_NUM >= 10200
+#include <libtorrent/download_priority.hpp>
+#include <libtorrent/write_resume_data.hpp>
+#endif
 
 #include <boost/bind.hpp>
+#include <boost/numeric/conversion/cast.hpp>
 
 #ifdef Q_OS_WIN
 #include <windows.h>
@@ -61,6 +66,7 @@
 #include "base/utils/fs.h"
 #include "base/utils/misc.h"
 #include "base/utils/string.h"
+#include "private/index.h"
 #include "peerinfo.h"
 #include "session.h"
 #include "trackerentry.h"
@@ -140,11 +146,11 @@ CreateTorrentParams::CreateTorrentParams(const AddTorrentParams &params)
 const qreal TorrentHandle::USE_GLOBAL_RATIO = -2.;
 const qreal TorrentHandle::NO_RATIO_LIMIT = -1.;
 
-const int TorrentHandle::USE_GLOBAL_SEEDING_TIME = -2;
-const int TorrentHandle::NO_SEEDING_TIME_LIMIT = -1;
+const std::chrono::minutes TorrentHandle::USE_GLOBAL_SEEDING_TIME {-2};
+const std::chrono::minutes TorrentHandle::NO_SEEDING_TIME_LIMIT {-1};
 
 const qreal TorrentHandle::MAX_RATIO = 9999.;
-const int TorrentHandle::MAX_SEEDING_TIME = 525600;
+const std::chrono::minutes TorrentHandle::MAX_SEEDING_TIME {525600};
 
 // The new libtorrent::create_torrent constructor appeared after 1.0.11 in RC_1_0
 // and after 1.1.1 in RC_1_1. Since it fixed an ABI incompatibility with previous versions
@@ -611,7 +617,7 @@ qreal TorrentHandle::ratioLimit() const
     return m_ratioLimit;
 }
 
-int TorrentHandle::seedingTimeLimit() const
+std::chrono::minutes TorrentHandle::seedingTimeLimit() const
 {
     return m_seedingTimeLimit;
 }
@@ -651,10 +657,9 @@ QStringList TorrentHandle::absoluteFilePathsUnwanted() const
 
     QDir saveDir(savePath(true));
     QStringList res;
-    std::vector<int> fp;
-    fp = m_nativeHandle.file_priorities();
 
-    int count = static_cast<int>(fp.size());
+    const auto fp = filePriorities();
+    const int count = fp.size();
     for (int i = 0; i < count; ++i) {
         if (fp[i] == 0) {
             const QString path = Utils::Fs::expandPathAbs(saveDir.absoluteFilePath(filePath(i)));
@@ -668,10 +673,17 @@ QStringList TorrentHandle::absoluteFilePathsUnwanted() const
 
 QVector<int> TorrentHandle::filePriorities() const
 {
-    std::vector<int> fp;
-    fp = m_nativeHandle.file_priorities();
-
-    return QVector<int>::fromStdVector(fp);
+#if LIBTORRENT_VERSION_NUM < 10200
+    return QVector<int>::fromStdVector(m_nativeHandle.file_priorities());
+#else
+    const auto fp = m_nativeHandle.get_file_priorities();
+    QVector<int> res(fp.size());
+    std::transform(fp.begin(), fp.end(), res.begin(), [](libt::download_priority_t p)
+    {
+        return static_cast<libt::download_priority_t::underlying_type>(p);
+    });
+    return res;
+#endif
 }
 
 TorrentInfo TorrentHandle::info() const
@@ -681,7 +693,12 @@ TorrentInfo TorrentHandle::info() const
 
 bool TorrentHandle::isPaused() const
 {
+#if LIBTORRENT_VERSION_NUM < 10200
     return (m_nativeStatus.paused && !m_nativeStatus.auto_managed);
+#else
+    return static_cast<bool>(m_nativeStatus.flags & libt::torrent_flags::paused) &&
+        !static_cast<bool>(m_nativeStatus.flags & libt::torrent_flags::auto_managed);
+#endif
 }
 
 bool TorrentHandle::isResumed() const
@@ -691,7 +708,11 @@ bool TorrentHandle::isResumed() const
 
 bool TorrentHandle::isQueued() const
 {
-    return (m_nativeStatus.paused && m_nativeStatus.auto_managed);
+#if LIBTORRENT_VERSION_NUM < 10200
+    return static_cast<bool>(m_nativeStatus.paused && m_nativeStatus.auto_managed);
+#else
+    return static_cast<bool>(m_nativeStatus.flags & libt::torrent_flags::paused & libt::torrent_flags::auto_managed);
+#endif
 }
 
 bool TorrentHandle::isChecking() const
@@ -773,12 +794,20 @@ bool TorrentHandle::isSeed() const
 
 bool TorrentHandle::isForced() const
 {
+#if LIBTORRENT_VERSION_NUM < 10200
     return (!m_nativeStatus.paused && !m_nativeStatus.auto_managed);
+#else
+    return !static_cast<bool>(m_nativeStatus.flags & (libt::torrent_flags::paused | libt::torrent_flags::auto_managed));
+#endif
 }
 
 bool TorrentHandle::isSequentialDownload() const
 {
+#if LIBTORRENT_VERSION_NUM < 10200
     return m_nativeStatus.sequential_download;
+#else
+    return static_cast<bool>(m_nativeStatus.flags & libt::torrent_flags::sequential_download);
+#endif
 }
 
 bool TorrentHandle::hasFirstLastPiecePriority() const
@@ -786,15 +815,24 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
     if (!hasMetadata())
         return m_needsToSetFirstLastPiecePriority;
 
+#if LIBTORRENT_VERSION_NUM < 10200
+    const int dont_download = 0;
+    const int top_priority = 7;
     const std::vector<int> filePriorities = nativeHandle().file_priorities();
-    for (int i = 0; i < static_cast<int>(filePriorities.size()); ++i) {
-        if (filePriorities[i] <= 0)
+#else
+    using libt::dont_download;
+    using libt::top_priority;
+    const std::vector<libt::download_priority_t> filePriorities = nativeHandle().get_file_priorities();
+#endif
+
+    for (std::size_t i = 0; i < filePriorities.size(); ++i) {
+        if (filePriorities[i] <= dont_download)
             continue;
 
         const TorrentInfo::PieceRange extremities = info().filePieces(i);
-        const int firstPiecePrio = nativeHandle().piece_priority(extremities.first());
-        const int lastPiecePrio = nativeHandle().piece_priority(extremities.last());
-        return ((firstPiecePrio == 7) && (lastPiecePrio == 7));
+        const auto firstPiecePrio = nativeHandle().piece_priority(makePieceIndex(extremities.first()));
+        const auto lastPiecePrio = nativeHandle().piece_priority(makePieceIndex(extremities.last()));
+        return ((firstPiecePrio == top_priority) && (lastPiecePrio == top_priority));
     }
 
     return false;
@@ -876,26 +914,41 @@ bool TorrentHandle::hasError() const
 {
 #if LIBTORRENT_VERSION_NUM < 10100
     return (m_nativeStatus.paused && !m_nativeStatus.error.empty());
-#else
+#elif LIBTORRENT_VERSION_NUM < 10200
     return (m_nativeStatus.paused && m_nativeStatus.errc);
+#else
+    return ((m_nativeStatus.flags & libt::torrent_flags::paused) && m_nativeStatus.errc);
 #endif
 }
 
 bool TorrentHandle::hasFilteredPieces() const
 {
+#if LIBTORRENT_VERSION_NUM < 10200
     std::vector<int> pp = m_nativeHandle.piece_priorities();
+    using download_priority_t = int;
+#else
+    const auto pp = m_nativeHandle.get_piece_priorities();
+    using libtorrent::download_priority_t; // TODO C++14 remove
+#endif
 
-    foreach (const int priority, pp)
-        if (priority == 0) return true;
-
-    return false;
+    return std::any_of(pp.begin(), pp.end(), [](download_priority_t priority)
+    {
+#if LIBTORRENT_VERSION_NUM < 10200
+        return priority == 0;
+#else
+        return priority == libt::dont_download;
+#endif
+    });
 }
 
 int TorrentHandle::queuePosition() const
 {
-    if (m_nativeStatus.queue_position < 0) return 0;
-
+    if (m_nativeStatus.queue_position < decltype(m_nativeStatus.queue_position)(0)) return 0;
+#if LIBTORRENT_VERSION_NUM < 10200
     return m_nativeStatus.queue_position + 1;
+#else
+    return static_cast<int>(m_nativeStatus.queue_position + decltype(m_nativeStatus.queue_position)::diff_type(1));
+#endif
 }
 
 QString TorrentHandle::error() const
@@ -917,22 +970,34 @@ qlonglong TorrentHandle::totalUpload() const
     return m_nativeStatus.all_time_upload;
 }
 
-int TorrentHandle::activeTime() const
+std::chrono::seconds TorrentHandle::activeTime() const
 {
-    return m_nativeStatus.active_time;
+#if LIBTORRENT_VERSION_NUM < 10200
+    return std::chrono::seconds(m_nativeStatus.active_time);
+#else
+    return m_nativeStatus.active_duration;
+#endif
 }
 
-int TorrentHandle::finishedTime() const
+std::chrono::seconds TorrentHandle::finishedTime() const
 {
-    return m_nativeStatus.finished_time;
+#if LIBTORRENT_VERSION_NUM < 10200
+    return std::chrono::seconds(m_nativeStatus.finished_time);
+#else
+    return m_nativeStatus.finished_duration;
+#endif
 }
 
-int TorrentHandle::seedingTime() const
+std::chrono::seconds TorrentHandle::seedingTime() const
 {
-    return m_nativeStatus.seeding_time;
+#if LIBTORRENT_VERSION_NUM < 10200
+    return std::chrono::seconds(m_nativeStatus.seeding_time);
+#else
+    return m_nativeStatus.seeding_duration;
+#endif
 }
 
-qulonglong TorrentHandle::eta() const
+std::chrono::seconds TorrentHandle::eta() const
 {
     if (isPaused()) return MAX_ETA;
 
@@ -940,10 +1005,10 @@ qulonglong TorrentHandle::eta() const
 
     if (isSeed()) {
         qreal maxRatioValue = maxRatio();
-        int maxSeedingTimeValue = maxSeedingTime();
-        if ((maxRatioValue < 0) && (maxSeedingTimeValue < 0)) return MAX_ETA;
+        auto maxSeedingTimeValue = maxSeedingTime();
+        if ((maxRatioValue < 0) && (maxSeedingTimeValue.count() < 0)) return MAX_ETA;
 
-        qlonglong ratioEta = MAX_ETA;
+        std::chrono::seconds ratioEta = MAX_ETA;
 
         if ((speedAverage.upload > 0) && (maxRatioValue >= 0)) {
 
@@ -951,15 +1016,15 @@ qulonglong TorrentHandle::eta() const
             if (realDL <= 0)
                 realDL = wantedSize();
 
-            ratioEta = ((realDL * maxRatioValue) - totalUpload()) / speedAverage.upload;
+            ratioEta = std::chrono::seconds(static_cast<std::chrono::seconds::rep>((((realDL * maxRatioValue) - totalUpload()) / speedAverage.upload)));
         }
 
-        qlonglong seedingTimeEta = MAX_ETA;
+        std::chrono::seconds seedingTimeEta = MAX_ETA;
 
-        if (maxSeedingTimeValue >= 0) {
+        if (maxSeedingTimeValue.count() >= 0) {
             seedingTimeEta = (maxSeedingTimeValue * 60) - seedingTime();
-            if (seedingTimeEta < 0)
-                seedingTimeEta = 0;
+            if (seedingTimeEta < seedingTimeEta.zero())
+                seedingTimeEta = seedingTimeEta.zero();
         }
 
         return qMin(ratioEta, seedingTimeEta);
@@ -967,7 +1032,7 @@ qulonglong TorrentHandle::eta() const
 
     if (!speedAverage.download) return MAX_ETA;
 
-    return (wantedSize() - completedSize()) / speedAverage.download;
+    return std::chrono::seconds(static_cast<std::chrono::seconds::rep>((wantedSize() - completedSize()) / speedAverage.download));
 }
 
 QVector<qreal> TorrentHandle::filesProgress() const
@@ -1047,21 +1112,29 @@ QDateTime TorrentHandle::completedTime() const
         return QDateTime();
 }
 
-int TorrentHandle::timeSinceUpload() const
+std::chrono::seconds TorrentHandle::timeSinceUpload() const
 {
-    return m_nativeStatus.time_since_upload;
+#if LIBTORRENT_VERSION_NUM < 10200
+    return std::chrono::seconds(m_nativeStatus.time_since_upload);
+#else
+    return std::chrono::duration_cast<std::chrono::seconds>(libt::clock_type::now() - m_nativeStatus.last_upload);
+#endif
 }
 
-int TorrentHandle::timeSinceDownload() const
+std::chrono::seconds TorrentHandle::timeSinceDownload() const
 {
-    return m_nativeStatus.time_since_download;
+#if LIBTORRENT_VERSION_NUM < 10200
+    return std::chrono::seconds(m_nativeStatus.time_since_download);
+#else
+    return std::chrono::duration_cast<std::chrono::seconds>(libt::clock_type::now() - m_nativeStatus.last_download);
+#endif
 }
 
-int TorrentHandle::timeSinceActivity() const
+std::chrono::seconds TorrentHandle::timeSinceActivity() const
 {
-    return ((m_nativeStatus.time_since_upload < 0) != (m_nativeStatus.time_since_download < 0))
-        ? std::max(m_nativeStatus.time_since_upload, m_nativeStatus.time_since_download)
-        : std::min(m_nativeStatus.time_since_upload, m_nativeStatus.time_since_download);
+    return ((timeSinceUpload() < std::chrono::seconds::zero()) != (timeSinceDownload() < std::chrono::seconds::zero()))
+        ? std::max(timeSinceUpload(), timeSinceDownload())
+        : std::min(timeSinceUpload(), timeSinceDownload());
 }
 
 int TorrentHandle::downloadLimit() const
@@ -1076,7 +1149,11 @@ int TorrentHandle::uploadLimit() const
 
 bool TorrentHandle::superSeeding() const
 {
+#if LIBTORRENT_VERSION_NUM < 10200
     return m_nativeStatus.super_seeding;
+#else
+    return static_cast<bool>(m_nativeStatus.flags & libt::torrent_flags::super_seeding);
+#endif
 }
 
 QList<PeerInfo> TorrentHandle::peers() const
@@ -1095,9 +1172,11 @@ QList<PeerInfo> TorrentHandle::peers() const
 QBitArray TorrentHandle::pieces() const
 {
     QBitArray result(m_nativeStatus.pieces.size());
+    int i = 0;
 
-    for (int i = 0; i < m_nativeStatus.pieces.size(); ++i)
-        result.setBit(i, m_nativeStatus.pieces.get_bit(i));
+    for (bool b : m_nativeStatus.pieces) {
+        result.setBit(i++, b);
+    }
 
     return result;
 }
@@ -1111,9 +1190,9 @@ QBitArray TorrentHandle::downloadingPieces() const
 
     std::vector<libt::partial_piece_info>::const_iterator it = queue.begin();
     std::vector<libt::partial_piece_info>::const_iterator itend = queue.end();
-    for (; it != itend; ++it)
-        result.setBit(it->piece_index);
-
+    for (; it != itend; ++it) {
+        result.setBit(indexValue(it->piece_index));
+    }
     return result;
 }
 
@@ -1138,12 +1217,12 @@ qreal TorrentHandle::maxRatio() const
     return m_ratioLimit;
 }
 
-int TorrentHandle::maxSeedingTime() const
+std::chrono::minutes TorrentHandle::maxSeedingTime() const
 {
     if (m_seedingTimeLimit == USE_GLOBAL_SEEDING_TIME)
-        return m_session->globalMaxSeedingMinutes();
+        return m_session->globalMaxSeedingTime();
 
-    return m_seedingTimeLimit;
+    return std::chrono::minutes(m_seedingTimeLimit);
 }
 
 qreal TorrentHandle::realRatio() const
@@ -1190,12 +1269,12 @@ int TorrentHandle::connectionsLimit() const
     return m_nativeStatus.connections_limit;
 }
 
-qlonglong TorrentHandle::nextAnnounce() const
+std::chrono::seconds TorrentHandle::nextAnnounce() const
 {
 #if LIBTORRENT_VERSION_NUM < 10100
-    return m_nativeStatus.next_announce.total_seconds();
+    return std::chrono::seconds(m_nativeStatus.next_announce.total_seconds());
 #else
-    return libt::duration_cast<libt::seconds>(m_nativeStatus.next_announce).count();
+    return std::chrono::duration_cast<std::chrono::seconds>(m_nativeStatus.next_announce);
 #endif
 }
 
@@ -1284,8 +1363,17 @@ void TorrentHandle::forceRecheck()
 void TorrentHandle::setSequentialDownload(bool b)
 {
     if (b != isSequentialDownload()) {
+#if LIBTORRENT_VERSION_NUM < 10200
         m_nativeHandle.set_sequential_download(b);
         m_nativeStatus.sequential_download = b; // prevent return cached value
+#else
+        if (b) {
+            m_nativeHandle.set_flags(libt::torrent_flags::sequential_download);
+        } else {
+            m_nativeHandle.unset_flags(libt::torrent_flags::sequential_download);
+        }
+        m_nativeStatus.flags = m_nativeHandle.flags();
+#endif
     }
 }
 
@@ -1310,22 +1398,42 @@ void TorrentHandle::setFirstLastPiecePriorityImpl(const bool enabled, const QVec
 
     // Updating file priorities is an async operation in libtorrent, when we just updated it and immediately query it
     // we might get the old/wrong values, so we rely on `updatedFilePrio` in this case.
-    const std::vector<int> filePriorities = !updatedFilePrio.isEmpty() ? updatedFilePrio.toStdVector() : nativeHandle().file_priorities();
-    std::vector<int> piecePriorities = nativeHandle().piece_priorities();
-    for (int index = 0; index < static_cast<int>(filePriorities.size()); ++index) {
-        const int filePrio = filePriorities[index];
-        if (filePrio <= 0)
+#if LIBTORRENT_VERSION_NUM < 10200
+    std::vector<int> filePriorities = !updatedFilePrio.isEmpty() ? updatedFilePrio.toStdVector() : m_nativeHandle.file_priorities();
+    std::vector<int> piecePriorities = m_nativeHandle.piece_priorities();
+    const int dont_download = 0;
+    const int top_priority = 7;
+#else
+    const auto convertPrioritiesVector = [](const QVector<int> &qv)
+    {
+        std::vector<libt::download_priority_t> res;
+        res.resize(static_cast<std::size_t>(qv.size()));
+        std::transform(qv.begin(), qv.end(), res.begin(), [](int p){return libt::download_priority_t(p);});
+        return res;
+    };
+
+    std::vector<libt::download_priority_t> filePriorities = !updatedFilePrio.isEmpty() ?
+        convertPrioritiesVector(updatedFilePrio) : m_nativeHandle.get_file_priorities();
+    std::vector<libt::download_priority_t> piecePriorities = m_nativeHandle.get_piece_priorities();
+    using libt::dont_download;
+    using libt::top_priority;
+#endif
+
+    // Download first and last pieces first for every file in the torrent
+    for (std::size_t index = 0; index < filePriorities.size(); ++index) {
+        const auto filePrio = filePriorities[index];
+        if (filePrio <= dont_download)
             continue;
 
         // Determine the priority to set
-        const int newPrio = enabled ? 7 : filePrio;
+        const auto newPrio = enabled ? top_priority : filePrio;
         const TorrentInfo::PieceRange extremities = info().filePieces(index);
 
         // worst case: AVI index = 1% of total file size (at the end of the file)
         const int nNumPieces = std::ceil(fileSize(index) * 0.01 / pieceLength());
         for (int i = 0; i < nNumPieces; ++i) {
-            piecePriorities[extremities.first() + i] = newPrio;
-            piecePriorities[extremities.last() - i] = newPrio;
+            piecePriorities[boost::numeric_cast<std::size_t>(extremities.first() + i)] = newPrio;
+            piecePriorities[boost::numeric_cast<std::size_t>(extremities.last() - i)] = newPrio;
         }
     }
 
@@ -1343,8 +1451,11 @@ void TorrentHandle::toggleFirstLastPiecePriority()
 void TorrentHandle::pause()
 {
     if (isPaused()) return;
-
+#if LIBTORRENT_VERSION_NUM < 10200
     m_nativeHandle.auto_managed(false);
+#else
+    m_nativeHandle.unset_flags(libt::torrent_flags::auto_managed);
+#endif
     m_nativeHandle.pause();
 
     // Libtorrent doesn't emit a torrent_paused_alert when the
@@ -1364,8 +1475,19 @@ void TorrentHandle::resume_impl(bool forced, bool uploadMode)
     if (hasError())
         m_nativeHandle.clear_error();
     m_hasMissingFiles = false;
+#if LIBTORRENT_VERSION_NUM < 10200
     m_nativeHandle.auto_managed(!forced);
     m_nativeHandle.set_upload_mode(uploadMode);
+#else
+    if (forced)
+        m_nativeHandle.unset_flags(libt::torrent_flags::auto_managed);
+    else
+        m_nativeHandle.set_flags(libt::torrent_flags::auto_managed);
+    if (uploadMode)
+        m_nativeHandle.set_flags(libt::torrent_flags::upload_mode);
+    else
+        m_nativeHandle.unset_flags(libt::torrent_flags::upload_mode);
+#endif
     m_nativeHandle.resume();
 }
 
@@ -1383,7 +1505,11 @@ void TorrentHandle::moveStorage(const QString &newPath, bool overwrite)
         qDebug("move storage: %s to %s", qUtf8Printable(oldPath), qUtf8Printable(newPath));
         // Actually move the storage
         m_nativeHandle.move_storage(newPath.toUtf8().constData()
+#if LIBTORRENT_VERSION_NUM < 10200
                                     , (overwrite ? libt::always_replace_files : libt::dont_replace));
+#else
+                                    , (overwrite ? libt::move_flags_t::always_replace_files : libt::move_flags_t::dont_replace));
+#endif
         m_moveStorageInfo.oldPath = oldPath;
         m_moveStorageInfo.newPath = newPath;
         updateState();
@@ -1402,7 +1528,7 @@ void TorrentHandle::renameFile(int index, const QString &name)
 {
     ++m_renameCount;
     qDebug() << Q_FUNC_INFO << index << name;
-    m_nativeHandle.rename_file(index, Utils::Fs::toNativePath(name).toStdString());
+    m_nativeHandle.rename_file(makeFileIndex(index), Utils::Fs::toNativePath(name).toStdString());
 }
 
 bool TorrentHandle::saveTorrentFile(const QString &path)
@@ -1423,11 +1549,11 @@ bool TorrentHandle::saveTorrentFile(const QString &path)
 
 void TorrentHandle::setFilePriority(int index, int priority)
 {
-    std::vector<int> priorities = m_nativeHandle.file_priorities();
+    QVector<int> priorities = filePriorities();
 
-    if ((priorities.size() > static_cast<quint64>(index)) && (priorities[index] != priority)) {
+    if ((priorities.size() > index) && (priorities[index] != priority)) {
         priorities[index] = priority;
-        prioritizeFiles(QVector<int>::fromStdVector(priorities));
+        prioritizeFiles(priorities);
     }
 }
 
@@ -1546,7 +1672,11 @@ void TorrentHandle::handleTrackerErrorAlert(const libtorrent::tracker_error_aler
 
     m_trackerInfos[trackerUrl].lastMessage = message;
 
+#if LIBTORRENT_VERSION_NUM < 10200
     if (p->status_code == 401)
+#else
+    if (p->error.category() == libt::http_category() && p->error.value() == 401)
+#endif
         m_session->handleTorrentTrackerAuthenticationRequired(this, trackerUrl);
 
     m_session->handleTorrentTrackerError(this, trackerUrl);
@@ -1622,10 +1752,18 @@ void TorrentHandle::handleTorrentResumedAlert(const libtorrent::torrent_resumed_
 
 void TorrentHandle::handleSaveResumeDataAlert(const libtorrent::save_resume_data_alert *p)
 {
-    const bool useDummyResumeData = !(p && p->resume_data);
-    libtorrent::entry dummyEntry;
+    const auto getAlertResumeData = [](const libtorrent::save_resume_data_alert *p)
+    {
+#if LIBTORRENT_VERSION_NUM < 10200
+        return *(p->resume_data);
+#else
+        return libt::write_resume_data(p->params);
+#endif
+    };
+
+    const bool useDummyResumeData = !p;
 
-    libtorrent::entry &resumeData = useDummyResumeData ? dummyEntry : *(p->resume_data);
+    libtorrent::entry resumeData = useDummyResumeData ? libtorrent::entry() : getAlertResumeData(p);
     if (useDummyResumeData) {
         resumeData["qBt-magnetUri"] = toMagnetUri().toStdString();
         resumeData["qBt-paused"] = isPaused();
@@ -1642,13 +1780,13 @@ void TorrentHandle::handleSaveResumeDataAlert(const libtorrent::save_resume_data
     }
     resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Profile::instance().toPortablePath(m_savePath).toStdString();
     resumeData["qBt-ratioLimit"] = static_cast<int>(m_ratioLimit * 1000);
-    resumeData["qBt-seedingTimeLimit"] = m_seedingTimeLimit;
+    resumeData["qBt-seedingTimeLimit"] = m_seedingTimeLimit.count();
     resumeData["qBt-category"] = m_category.toStdString();
     resumeData["qBt-tags"] = setToEntryList(m_tags);
     resumeData["qBt-name"] = m_name.toStdString();
     resumeData["qBt-seedStatus"] = m_hasSeedStatus;
     resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
-    resumeData["qBt-queuePosition"] = (nativeHandle().queue_position() + 1); // qBt starts queue at 1
+    resumeData["qBt-queuePosition"] = (static_cast<int>(nativeHandle().queue_position()) + 1); // qBt starts queue at 1
     resumeData["qBt-hasRootFolder"] = m_hasRootFolder;
 
     m_session->handleTorrentResumeDataReady(this, resumeData);
@@ -1689,7 +1827,7 @@ void TorrentHandle::handleFileRenamedAlert(const libtorrent::file_renamed_alert
     // TODO: Check this!
     if (filesCount() > 1) {
         // Check if folders were renamed
-        QStringList oldPathParts = m_torrentInfo.origFilePath(p->index).split('/');
+        QStringList oldPathParts = m_torrentInfo.origFilePath(indexValue(p->index)).split("/");
         oldPathParts.removeLast();
         QString oldPath = oldPathParts.join('/');
         QStringList newPathParts = newName.split('/');
@@ -1725,12 +1863,12 @@ void TorrentHandle::handleFileCompletedAlert(const libtorrent::file_completed_al
 
     qDebug("A file completed download in torrent \"%s\"", qUtf8Printable(name()));
     if (m_session->isAppendExtensionEnabled()) {
-        QString name = filePath(p->index);
+        QString name = filePath(indexValue(p->index));
         if (name.endsWith(QB_EXT)) {
             const QString oldName = name;
             name.chop(QB_EXT.size());
             qDebug("Renaming %s to %s", qUtf8Printable(oldName), qUtf8Printable(name));
-            renameFile(p->index, name);
+            renameFile(indexValue(p->index), name);
         }
     }
 }
@@ -1958,7 +2096,7 @@ void TorrentHandle::setRatioLimit(qreal limit)
     }
 }
 
-void TorrentHandle::setSeedingTimeLimit(int limit)
+void TorrentHandle::setSeedingTimeLimit(std::chrono::minutes limit)
 {
     if (limit < USE_GLOBAL_SEEDING_TIME)
         limit = NO_SEEDING_TIME_LIMIT;
@@ -1983,7 +2121,14 @@ void TorrentHandle::setDownloadLimit(int limit)
 
 void TorrentHandle::setSuperSeeding(bool enable)
 {
+#if LIBTORRENT_VERSION_NUM < 10200
     m_nativeHandle.super_seeding(enable);
+#else
+    if (enable)
+        m_nativeHandle.set_flags(libt::torrent_flags::super_seeding);
+    else
+        m_nativeHandle.unset_flags(libt::torrent_flags::super_seeding);
+#endif
     if (superSeeding() != enable)
         updateStatus();
 }
@@ -2018,7 +2163,16 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
     }
 
     qDebug() << Q_FUNC_INFO << "Changing files priorities...";
+#if LIBTORRENT_VERSION_NUM < 10200
     m_nativeHandle.prioritize_files(priorities.toStdVector());
+#else
+    std::vector<libt::download_priority_t> fp(static_cast<std::size_t>(priorities.size()));
+    std::transform(priorities.begin(), priorities.end(), fp.begin(), [](int p)
+    {
+        return libt::download_priority_t(p);
+    });
+    m_nativeHandle.prioritize_files(fp);
+#endif
 
     qDebug() << Q_FUNC_INFO << "Moving unwanted files to .unwanted folder and conversely...";
     QString spath = savePath(true);
diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h
index 9212483e1..8b596e750 100644
--- a/src/base/bittorrent/torrenthandle.h
+++ b/src/base/bittorrent/torrenthandle.h
@@ -47,6 +47,7 @@
 #include <boost/function.hpp>
 
 #include "base/tristatebool.h"
+#include "base/types.h"
 #include "private/speedmonitor.h"
 #include "infohash.h"
 #include "torrentinfo.h"
@@ -57,6 +58,9 @@ template<typename T, typename U> struct QPair;
 
 extern const QString QB_EXT;
 
+#if LIBTORRENT_VERSION_NUM >= 10108 // #if __has_include(<libtorrent/fwd.hpp>)
+#include <libtorrent/fwd.hpp>
+#else
 namespace libtorrent
 {
     class alert;
@@ -79,6 +83,7 @@ namespace libtorrent
     struct fastresume_rejected_alert;
     struct torrent_status;
 }
+#endif
 
 namespace BitTorrent
 {
@@ -110,7 +115,7 @@ namespace BitTorrent
         QVector<int> filePriorities;
         // for restored torrents
         qreal ratioLimit;
-        int seedingTimeLimit;
+        std::chrono::minutes seedingTimeLimit;
 
         CreateTorrentParams();
         CreateTorrentParams(const AddTorrentParams &params);
@@ -164,11 +169,11 @@ namespace BitTorrent
         static const qreal USE_GLOBAL_RATIO;
         static const qreal NO_RATIO_LIMIT;
 
-        static const int USE_GLOBAL_SEEDING_TIME;
-        static const int NO_SEEDING_TIME_LIMIT;
+        static const std::chrono::minutes USE_GLOBAL_SEEDING_TIME;
+        static const std::chrono::minutes NO_SEEDING_TIME_LIMIT;
 
         static const qreal MAX_RATIO;
-        static const int MAX_SEEDING_TIME;
+        static const std::chrono::minutes MAX_SEEDING_TIME;
 
         TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
                           const CreateTorrentParams &params);
@@ -258,7 +263,7 @@ namespace BitTorrent
         qreal progress() const;
         QDateTime addedTime() const;
         qreal ratioLimit() const;
-        int seedingTimeLimit() const;
+        std::chrono::minutes seedingTimeLimit() const;
 
         QString filePath(int index) const;
         QString fileName(int index) const;
@@ -294,10 +299,10 @@ namespace BitTorrent
         QString error() const;
         qlonglong totalDownload() const;
         qlonglong totalUpload() const;
-        int activeTime() const;
-        int finishedTime() const;
-        int seedingTime() const;
-        qulonglong eta() const;
+        std::chrono::seconds activeTime() const;
+        std::chrono::seconds finishedTime() const;
+        std::chrono::seconds seedingTime() const;
+        std::chrono::seconds eta() const;
         QVector<qreal> filesProgress() const;
         int seedsCount() const;
         int peersCount() const;
@@ -309,9 +314,9 @@ namespace BitTorrent
         int incompleteCount() const;
         QDateTime lastSeenComplete() const;
         QDateTime completedTime() const;
-        int timeSinceUpload() const;
-        int timeSinceDownload() const;
-        int timeSinceActivity() const;
+        std::chrono::seconds timeSinceUpload() const;
+        std::chrono::seconds timeSinceDownload() const;
+        std::chrono::seconds timeSinceActivity() const;
         int downloadLimit() const;
         int uploadLimit() const;
         bool superSeeding() const;
@@ -321,7 +326,7 @@ namespace BitTorrent
         QVector<int> pieceAvailability() const;
         qreal distributedCopies() const;
         qreal maxRatio() const;
-        int maxSeedingTime() const;
+        std::chrono::minutes maxSeedingTime() const;
         qreal realRatio() const;
         int uploadPayloadRate() const;
         int downloadPayloadRate() const;
@@ -329,7 +334,7 @@ namespace BitTorrent
         qlonglong totalPayloadDownload() const;
         int connectionsCount() const;
         int connectionsLimit() const;
-        qlonglong nextAnnounce() const;
+        std::chrono::seconds nextAnnounce() const;
 
         void setName(const QString &name);
         void setSequentialDownload(bool b);
@@ -350,7 +355,7 @@ namespace BitTorrent
         void prioritizeFiles(const QVector<int> &priorities);
         void setFilePriority(int index, int priority);
         void setRatioLimit(qreal limit);
-        void setSeedingTimeLimit(int limit);
+        void setSeedingTimeLimit(std::chrono::minutes limit);
         void setUploadLimit(int limit);
         void setDownloadLimit(int limit);
         void setSuperSeeding(bool enable);
@@ -456,7 +461,7 @@ namespace BitTorrent
         QSet<QString> m_tags;
         bool m_hasSeedStatus;
         qreal m_ratioLimit;
-        int m_seedingTimeLimit;
+        std::chrono::minutes m_seedingTimeLimit;
         bool m_tempPathDisabled;
         bool m_hasMissingFiles;
         bool m_hasRootFolder;
diff --git a/src/base/bittorrent/torrentinfo.cpp b/src/base/bittorrent/torrentinfo.cpp
index df96014a6..4f1c7d8f7 100644
--- a/src/base/bittorrent/torrentinfo.cpp
+++ b/src/base/bittorrent/torrentinfo.cpp
@@ -35,9 +35,15 @@
 #include <QString>
 #include <QUrl>
 
+#include <boost/optional.hpp>
+
+#include <libtorrent/error_code.hpp>
+#include <libtorrent/version.hpp>
+
 #include "base/utils/fs.h"
 #include "base/utils/misc.h"
 #include "base/utils/string.h"
+#include "private/index.h"
 #include "infohash.h"
 #include "trackerentry.h"
 
@@ -45,8 +51,12 @@ namespace libt = libtorrent;
 using namespace BitTorrent;
 
 TorrentInfo::TorrentInfo(NativeConstPtr nativeInfo)
+#if LIBTORRENT_VERSION_NUM >= 10200
+    : m_nativeInfo {std::const_pointer_cast<libt::torrent_info>(nativeInfo)}
+#else
+    : m_nativeInfo {boost::const_pointer_cast<libt::torrent_info>(nativeInfo)}
+#endif
 {
-    m_nativeInfo = boost::const_pointer_cast<libt::torrent_info>(nativeInfo);
 }
 
 TorrentInfo::TorrentInfo(const TorrentInfo &other)
@@ -195,7 +205,7 @@ int TorrentInfo::pieceLength() const
 int TorrentInfo::pieceLength(int index) const
 {
     if (!isValid()) return -1;
-    return m_nativeInfo->piece_size(index);
+    return m_nativeInfo->piece_size(makePieceIndex(index));
 }
 
 int TorrentInfo::piecesCount() const
@@ -207,7 +217,7 @@ int TorrentInfo::piecesCount() const
 QString TorrentInfo::filePath(int index) const
 {
     if (!isValid()) return QString();
-    return Utils::Fs::fromNativePath(QString::fromStdString(m_nativeInfo->files().file_path(index)));
+    return Utils::Fs::fromNativePath(QString::fromStdString(m_nativeInfo->files().file_path(makeFileIndex(index))));
 }
 
 QStringList TorrentInfo::filePaths() const
@@ -227,19 +237,19 @@ QString TorrentInfo::fileName(int index) const
 QString TorrentInfo::origFilePath(int index) const
 {
     if (!isValid()) return QString();
-    return Utils::Fs::fromNativePath(QString::fromStdString(m_nativeInfo->orig_files().file_path(index)));
+    return Utils::Fs::fromNativePath(QString::fromStdString(m_nativeInfo->orig_files().file_path(makeFileIndex(index))));
 }
 
 qlonglong TorrentInfo::fileSize(int index) const
 {
     if (!isValid()) return -1;
-    return m_nativeInfo->files().file_size(index);
+    return m_nativeInfo->files().file_size(makeFileIndex(index));
 }
 
 qlonglong TorrentInfo::fileOffset(int index) const
 {
     if (!isValid()) return -1;
-    return m_nativeInfo->files().file_offset(index);
+    return m_nativeInfo->files().file_offset(makeFileIndex(index));
 }
 
 QList<TrackerEntry> TorrentInfo::trackers() const
@@ -290,11 +300,14 @@ QVector<int> TorrentInfo::fileIndicesForPiece(int pieceIndex) const
         return QVector<int>();
 
     std::vector<libt::file_slice> files(
-        nativeInfo()->map_block(pieceIndex, 0, nativeInfo()->piece_size(pieceIndex)));
+        nativeInfo()->map_block(makePieceIndex(pieceIndex), 0, nativeInfo()->piece_size(makePieceIndex(pieceIndex))));
     QVector<int> res;
     res.reserve(int(files.size()));
     std::transform(files.begin(), files.end(), std::back_inserter(res),
-        [](const libt::file_slice &s) { return s.file_index; });
+        [](const libt::file_slice &s)
+        {
+            return indexValue(s.file_index);
+        });
 
     return res;
 }
@@ -309,7 +322,11 @@ QVector<QByteArray> TorrentInfo::pieceHashes() const
     hashes.reserve(count);
 
     for (int i = 0; i < count; ++i)
+#if LIBTORRENT_VERSION_NUM < 10200
         hashes += { m_nativeInfo->hash_for_piece_ptr(i), libtorrent::sha1_hash::size };
+#else
+        hashes += { m_nativeInfo->hash_for_piece_ptr(makePieceIndex(i)), static_cast<int>(libtorrent::sha1_hash::size()) };
+#endif
 
     return hashes;
 }
@@ -338,16 +355,16 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(int fileIndex) const
     }
 
     const libt::file_storage &files = nativeInfo()->files();
-    const auto fileSize = files.file_size(fileIndex);
-    const auto fileOffset = files.file_offset(fileIndex);
-    return makeInterval(static_cast<int>(fileOffset / pieceLength()),
-                        static_cast<int>((fileOffset + fileSize - 1) / pieceLength()));
+    const auto fileSize = files.file_size(makeFileIndex(fileIndex));
+    const auto firstOffset = files.file_offset(makeFileIndex(fileIndex));
+    return makeInterval(static_cast<int>(firstOffset / pieceLength()),
+                        static_cast<int>((firstOffset + fileSize - 1) / pieceLength()));
 }
 
 void TorrentInfo::renameFile(const int index, const QString &newPath)
 {
     if (!isValid()) return;
-    nativeInfo()->rename_file(index, Utils::Fs::toNativePath(newPath).toStdString());
+    nativeInfo()->rename_file(makeFileIndex(index), Utils::Fs::toNativePath(newPath).toStdString());
 }
 
 int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
@@ -397,7 +414,7 @@ void TorrentInfo::stripRootFolder()
     if (files.name() != testName) {
         files.set_name(testName);
         for (int i = 0; i < files.num_files(); ++i)
-            files.rename_file(i, files.file_path(i));
+            files.rename_file(makeFileIndex(i), files.file_path(makeFileIndex(i)));
     }
 
     files.set_name("");
diff --git a/src/base/bittorrent/torrentinfo.h b/src/base/bittorrent/torrentinfo.h
index d3b11f283..04a15b73c 100644
--- a/src/base/bittorrent/torrentinfo.h
+++ b/src/base/bittorrent/torrentinfo.h
@@ -55,7 +55,10 @@ namespace BitTorrent
         Q_DECLARE_TR_FUNCTIONS(TorrentInfo)
 
     public:
-#if LIBTORRENT_VERSION_NUM < 10100
+#if LIBTORRENT_VERSION_NUM >= 10200
+        typedef std::shared_ptr<const libtorrent::torrent_info> NativeConstPtr;
+        typedef std::shared_ptr<libtorrent::torrent_info> NativePtr;
+#elif LIBTORRENT_VERSION_NUM < 10100
         typedef boost::intrusive_ptr<const libtorrent::torrent_info> NativeConstPtr;
         typedef boost::intrusive_ptr<libtorrent::torrent_info> NativePtr;
 #else
diff --git a/src/base/bittorrent/trackerentry.cpp b/src/base/bittorrent/trackerentry.cpp
index a757709bb..2ecdcb736 100644
--- a/src/base/bittorrent/trackerentry.cpp
+++ b/src/base/bittorrent/trackerentry.cpp
@@ -33,6 +33,45 @@
 #include "base/utils/misc.h"
 #include "base/utils/string.h"
 
+#include <algorithm>
+
+namespace
+{
+    bool isEntryWorking(const libtorrent::announce_entry& entry)
+    {
+#if LIBTORRENT_VERSION_NUM < 10200
+    // libtorrent::announce_entry::is_working() returns
+    // true when the tracker hasn't been tried yet.
+    return entry.verified && entry.is_working();
+#else
+    return entry.verified && std::any_of(entry.endpoints.begin(), entry.endpoints.end(),
+                                    [](libtorrent::announce_endpoint const& aep) { return aep.is_working(); });
+#endif
+    }
+
+    bool isEntryFailing(const libtorrent::announce_entry& entry)
+    {
+#if LIBTORRENT_VERSION_NUM < 10200
+        return entry.fails > 0;
+#else
+        return std::any_of(entry.endpoints.begin(), entry.endpoints.end(),
+            [](const libtorrent::announce_endpoint& aep) { return aep.fails > 0; }
+        );
+#endif
+    }
+
+    bool isEntryUpdating(const libtorrent::announce_entry& entry)
+    {
+#if LIBTORRENT_VERSION_NUM < 10200
+        return entry.updating;
+#else
+        return std::any_of(entry.endpoints.begin(), entry.endpoints.end(),
+            [](const libtorrent::announce_endpoint& aep) { return aep.updating; }
+        );
+#endif
+    }
+}
+
 using namespace BitTorrent;
 
 TrackerEntry::TrackerEntry(const QString &url)
@@ -62,16 +101,11 @@ int TrackerEntry::tier() const
 
 TrackerEntry::Status TrackerEntry::status() const
 {
-    // libtorrent::announce_entry::is_working() returns
-    // true when the tracker hasn't been tried yet.
-    if (m_nativeEntry.verified && m_nativeEntry.is_working())
+    if (isEntryWorking(m_nativeEntry))
         return Working;
-    else if ((m_nativeEntry.fails == 0) && m_nativeEntry.updating)
-        return Updating;
-    else if (m_nativeEntry.fails == 0)
-        return NotContacted;
-    else
-        return NotWorking;
+    if (!isEntryFailing(m_nativeEntry))
+        return isEntryUpdating(m_nativeEntry) ? Updating : NotContacted;
+    return NotWorking;
 }
 
 void TrackerEntry::setTier(int value)
diff --git a/src/base/net/portforwarder.cpp b/src/base/net/portforwarder.cpp
index 3cf926eaa..0e6379e25 100644
--- a/src/base/net/portforwarder.cpp
+++ b/src/base/net/portforwarder.cpp
@@ -31,7 +31,6 @@
 #include <QDebug>
 
 #include <libtorrent/session.hpp>
-#include <libtorrent/version.hpp>
 
 #include "base/logger.h"
 #include "base/settingsstorage.h"
@@ -94,7 +93,11 @@ void PortForwarder::setEnabled(bool enabled)
 void PortForwarder::addPort(quint16 port)
 {
     if (!m_mappedPorts.contains(port)) {
+#if LIBTORRENT_VERSION_NUM < 10200
         m_mappedPorts.insert(port, 0);
+#else
+        m_mappedPorts.insert(port, std::vector<libtorrent::port_mapping_t>());
+#endif
         if (m_active)
             m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port);
     }
@@ -104,7 +107,13 @@ void PortForwarder::deletePort(quint16 port)
 {
     if (m_mappedPorts.contains(port)) {
         if (m_active)
+#if LIBTORRENT_VERSION_NUM < 10200
             m_provider->delete_port_mapping(m_mappedPorts[port]);
+#else
+            for (const auto& mapping: m_mappedPorts[port]) {
+                m_provider->delete_port_mapping(mapping);
+            }
+#endif
         m_mappedPorts.remove(port);
     }
 }
diff --git a/src/base/net/portforwarder.h b/src/base/net/portforwarder.h
index 86ce8d028..c4a3c4702 100644
--- a/src/base/net/portforwarder.h
+++ b/src/base/net/portforwarder.h
@@ -29,9 +29,19 @@
 #ifndef NET_PORTFORWARDER_H
 #define NET_PORTFORWARDER_H
 
+#include <libtorrent/version.hpp>
+#if LIBTORRENT_VERSION_NUM >= 10200
+#include <libtorrent/portmap.hpp>
+#endif
+
 #include <QHash>
 #include <QObject>
 
+#include <libtorrent/version.hpp>
+#if LIBTORRENT_VERSION_NUM >= 10200
+#include <libtorrent/portmap.hpp>
+#endif
+
 namespace libtorrent
 {
     class session;
@@ -64,8 +74,11 @@ namespace Net
 
         bool m_active;
         libtorrent::session *m_provider;
+#if LIBTORRENT_VERSION_NUM < 10200
         QHash<quint16, int> m_mappedPorts;
-
+#else
+        QHash<quint16, std::vector<libtorrent::port_mapping_t>> m_mappedPorts;
+#endif
         static PortForwarder *m_instance;
     };
 }
diff --git a/src/base/types.h b/src/base/types.h
index bd18b1a40..07ba399c3 100644
--- a/src/base/types.h
+++ b/src/base/types.h
@@ -30,8 +30,15 @@
 #define TYPES_H
 
 #include <QMap>
+#include <QMetaType>
 
-const qlonglong MAX_ETA = 8640000;
+#include <chrono>
+
+Q_DECLARE_METATYPE(std::chrono::seconds)
+Q_DECLARE_METATYPE(std::chrono::minutes)
+Q_DECLARE_METATYPE(std::chrono::hours)
+
+const std::chrono::seconds MAX_ETA {8640000};
 
 enum class ShutdownDialogAction
 {
diff --git a/src/base/utils/misc.cpp b/src/base/utils/misc.cpp
index 67ec2ec89..2632bcc18 100644
--- a/src/base/utils/misc.cpp
+++ b/src/base/utils/misc.cpp
@@ -345,30 +345,37 @@ bool Utils::Misc::isPreviewable(const QString &extension)
 
 // Take a number of seconds and return an user-friendly
 // time duration like "1d 2h 10m".
-QString Utils::Misc::userFriendlyDuration(qlonglong seconds)
+QString Utils::Misc::userFriendlyDuration(std::chrono::seconds seconds)
 {
-    if ((seconds < 0) || (seconds >= MAX_ETA))
+    Q_ASSERT(seconds > seconds.zero());
+
+    // TODO C++14 min, hour literals
+    // TODO C++20 days duration
+    using std::chrono::duration_cast;
+
+    if (seconds >= MAX_ETA)
         return QString::fromUtf8(C_INFINITY);
 
-    if (seconds == 0)
+    if (seconds.count() == 0)
         return "0";
 
-    if (seconds < 60)
+    if (seconds < std::chrono::minutes{1})
         return QCoreApplication::translate("misc", "< 1m", "< 1 minute");
 
-    qlonglong minutes = seconds / 60;
-    if (minutes < 60)
-        return QCoreApplication::translate("misc", "%1m", "e.g: 10minutes").arg(QString::number(minutes));
+    auto minutes = duration_cast<std::chrono::minutes>(seconds);
+    if (minutes < std::chrono::hours{1})
+        return QCoreApplication::translate("misc", "%1m", "e.g: 10minutes").arg(QString::number(minutes.count()));
 
-    qlonglong hours = minutes / 60;
-    minutes -= hours * 60;
-    if (hours < 24)
-        return QCoreApplication::translate("misc", "%1h %2m", "e.g: 3hours 5minutes").arg(QString::number(hours), QString::number(minutes));
+    auto hours = duration_cast<std::chrono::hours>(minutes);
+    minutes -= hours;
+    if (hours.count() < 24)
+        return QCoreApplication::translate("misc", "%1h %2m", "e.g: 3hours 5minutes")
+            .arg(QString::number(hours.count()), QString::number(minutes.count()));
 
-    qlonglong days = hours / 24;
-    hours -= days * 24;
+    auto days = hours.count() / 24;
+    hours -= std::chrono::hours(days * 24);
     if (days < 100)
-        return QCoreApplication::translate("misc", "%1d %2h", "e.g: 2days 10hours").arg(QString::number(days), QString::number(hours));
+        return QCoreApplication::translate("misc", "%1d %2h", "e.g: 2days 10hours").arg(QString::number(days), QString::number(hours.count()));
 
     return QString::fromUtf8(C_INFINITY);
 }
diff --git a/src/base/utils/misc.h b/src/base/utils/misc.h
index f4f06461b..32d5e530c 100644
--- a/src/base/utils/misc.h
+++ b/src/base/utils/misc.h
@@ -29,7 +29,9 @@
 #ifndef UTILS_MISC_H
 #define UTILS_MISC_H
 
+#include <chrono>
 #include <ctime>
+#include <type_traits>
 #include <vector>
 
 #include <QtGlobal>
@@ -54,6 +56,12 @@ namespace Utils
 {
     namespace Misc
     {
+        namespace impl
+        {
+            template <typename T> struct is_duration : std::false_type {};
+            template <typename Rep, typename Period> struct is_duration<
+                std::chrono::duration<Rep, Period>> : std::true_type {};
+        }
         // use binary prefix standards from IEC 60027-2
         // see http://en.wikipedia.org/wiki/Kilobyte
         enum class SizeUnit
@@ -90,9 +98,18 @@ namespace Utils
 
         bool isPreviewable(const QString &extension);
 
-        // Take a number of seconds and return a user-friendly
+        // Takes a number of seconds and return a user-friendly
         // time duration like "1d 2h 10m".
-        QString userFriendlyDuration(qlonglong seconds);
+        QString userFriendlyDuration(std::chrono::seconds seconds);
+
+        template <typename Duration, typename = typename std::enable_if<impl::is_duration<Duration>::value>::type>
+        inline QString friendlySpeed(qint64 bytesValue, Duration interval)
+        {
+            Q_ASSERT(interval.count() >= 0);
+            return friendlyUnit(bytesValue /
+                (interval.count() > 0 ? std::chrono::duration_cast<std::chrono::seconds>(interval).count() : 1));
+        }
+
         QString getUserIDString();
 
         // Convert functions
diff --git a/src/config.h.cmakein b/src/config.h.cmakein
index 5d6a30c32..9c5c23835 100644
--- a/src/config.h.cmakein
+++ b/src/config.h.cmakein
@@ -12,3 +12,4 @@
 #endif
 
 #cmakedefine STACKTRACE_WIN
+
diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp
index fd59048d8..3859a9246 100644
--- a/src/gui/advancedsettings.cpp
+++ b/src/gui/advancedsettings.cpp
@@ -87,7 +87,9 @@ enum AdvSettingsRows
     DISK_CACHE,
     DISK_CACHE_TTL,
     OS_CACHE,
+#if LIBTORRENT_VERSION_NUM < 10200
     GUIDED_READ_CACHE,
+#endif
 #if LIBTORRENT_VERSION_NUM >= 10107
     COALESCE_RW,
 #endif
@@ -347,9 +349,11 @@ void AdvancedSettings::loadAdvancedSettings()
     // Enable OS cache
     checkBoxOsCache.setChecked(session->useOSCache());
     addRow(OS_CACHE, tr("Enable OS cache"), &checkBoxOsCache);
+#if LIBTORRENT_VERSION_NUM < 10200
     // Guided read cache
     checkBoxGuidedReadCache.setChecked(session->isGuidedReadCacheEnabled());
     addRow(GUIDED_READ_CACHE, tr("Guided read cache"), &checkBoxGuidedReadCache);
+#endif
     // Coalesce reads & writes
     checkBoxCoalesceRW.setChecked(session->isCoalesceReadWriteEnabled());
 #if LIBTORRENT_VERSION_NUM >= 10107
diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp
index ffb880824..b33b4fe8e 100644
--- a/src/gui/optionsdialog.cpp
+++ b/src/gui/optionsdialog.cpp
@@ -684,7 +684,7 @@ void OptionsDialog::saveOptions()
     session->setAddTrackersEnabled(m_ui->checkEnableAddTrackers->isChecked());
     session->setAdditionalTrackers(m_ui->textTrackers->toPlainText());
     session->setGlobalMaxRatio(getMaxRatio());
-    session->setGlobalMaxSeedingMinutes(getMaxSeedingMinutes());
+    session->setGlobalMaxSeedingTime(getMaxSeedingTime());
     session->setMaxRatioAction(static_cast<MaxRatioAction>(m_ui->comboRatioLimitAct->currentIndex()));
     // End Bittorrent preferences
 
@@ -1100,18 +1100,19 @@ void OptionsDialog::loadOptions()
         m_ui->checkMaxRatio->setChecked(false);
         m_ui->spinMaxRatio->setEnabled(false);
     }
-    if (session->globalMaxSeedingMinutes() >= 0) {
+    if (session->globalMaxSeedingTime().count() >= 0) {
         // Enable
         m_ui->checkMaxSeedingMinutes->setChecked(true);
         m_ui->spinMaxSeedingMinutes->setEnabled(true);
-        m_ui->spinMaxSeedingMinutes->setValue(session->globalMaxSeedingMinutes());
+        m_ui->spinMaxSeedingMinutes->setValue(
+            std::chrono::duration_cast<std::chrono::minutes>(session->globalMaxSeedingTime()).count());
     }
     else {
         // Disable
         m_ui->checkMaxSeedingMinutes->setChecked(false);
         m_ui->spinMaxSeedingMinutes->setEnabled(false);
     }
-    m_ui->comboRatioLimitAct->setEnabled((session->globalMaxSeedingMinutes() >= 0) || (session->globalMaxRatio() >= 0.));
+    m_ui->comboRatioLimitAct->setEnabled((session->globalMaxSeedingTime().count() >= 0) || (session->globalMaxRatio() >= 0.));
     m_ui->comboRatioLimitAct->setCurrentIndex(session->maxRatioAction());
     // End Bittorrent preferences
 
@@ -1256,11 +1257,11 @@ qreal OptionsDialog::getMaxRatio() const
 }
 
 // Return Seeding Minutes
-int OptionsDialog::getMaxSeedingMinutes() const
+std::chrono::minutes OptionsDialog::getMaxSeedingTime() const
 {
     if (m_ui->checkMaxSeedingMinutes->isChecked())
-        return m_ui->spinMaxSeedingMinutes->value();
-    return -1;
+        return std::chrono::minutes {m_ui->spinMaxSeedingMinutes->value()};
+    return std::chrono::minutes {-1};
 }
 
 // Return max connections number
diff --git a/src/gui/optionsdialog.h b/src/gui/optionsdialog.h
index bac2945d7..c779d927f 100644
--- a/src/gui/optionsdialog.h
+++ b/src/gui/optionsdialog.h
@@ -29,6 +29,8 @@
 #ifndef OPTIONSDIALOG_H
 #define OPTIONSDIALOG_H
 
+#include <chrono>
+
 #include <QButtonGroup>
 #include <QDialog>
 
@@ -148,7 +150,7 @@ private:
     bool isLSDEnabled() const;
     int getEncryptionSetting() const;
     qreal getMaxRatio() const;
-    int getMaxSeedingMinutes() const;
+    std::chrono::minutes getMaxSeedingTime() const;
     // Proxy options
     bool isProxyEnabled() const;
     bool isProxyAuthEnabled() const;
diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp
index 5e8e16dbb..28663b52f 100644
--- a/src/gui/properties/propertieswidget.cpp
+++ b/src/gui/properties/propertieswidget.cpp
@@ -442,11 +442,11 @@ void PropertiesWidget::loadDynamicData()
 
             m_ui->labelDlSpeedVal->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
                 .arg(Utils::Misc::friendlyUnit(m_torrent->downloadPayloadRate(), true)
-                    , Utils::Misc::friendlyUnit(m_torrent->totalDownload() / (1 + m_torrent->activeTime() - m_torrent->finishedTime()), true)));
+                    , Utils::Misc::friendlySpeed(m_torrent->totalDownload(), m_torrent->activeTime() - m_torrent->finishedTime())));
 
             m_ui->labelUpSpeedVal->setText(tr("%1 (%2 avg.)", "%1 and %2 are speed rates, e.g. 200KiB/s (100KiB/s avg.)")
                 .arg(Utils::Misc::friendlyUnit(m_torrent->uploadPayloadRate(), true)
-                    , Utils::Misc::friendlyUnit(m_torrent->totalUpload() / (1 + m_torrent->activeTime()), true)));
+                    , Utils::Misc::friendlySpeed(m_torrent->totalUpload(), m_torrent->activeTime())));
 
             m_ui->labelLastSeenCompleteVal->setText(m_torrent->lastSeenComplete().isValid() ? m_torrent->lastSeenComplete().toString(Qt::DefaultLocaleShortDate) : tr("Never"));
 
diff --git a/src/gui/properties/trackerlistwidget.cpp b/src/gui/properties/trackerlistwidget.cpp
index 986412ae4..30df07279 100644
--- a/src/gui/properties/trackerlistwidget.cpp
+++ b/src/gui/properties/trackerlistwidget.cpp
@@ -52,6 +52,8 @@
 #include "propertieswidget.h"
 #include "trackersadditiondialog.h"
 
+#include <numeric>
+
 TrackerListWidget::TrackerListWidget(PropertiesWidget *properties)
     : QTreeWidget()
     , m_properties(properties)
@@ -366,10 +368,49 @@ void TrackerListWidget::loadTrackers()
         }
 
         item->setText(COL_RECEIVED, QString::number(data.numPeers));
-#if LIBTORRENT_VERSION_NUM >= 10000
-        item->setText(COL_SEEDS, (entry.nativeEntry().scrape_complete > -1) ? QString::number(entry.nativeEntry().scrape_complete) : tr("N/A"));
-        item->setText(COL_PEERS, (entry.nativeEntry().scrape_incomplete > -1) ? QString::number(entry.nativeEntry().scrape_incomplete) : tr("N/A"));
-        item->setText(COL_DOWNLOADED, (entry.nativeEntry().scrape_downloaded > -1) ? QString::number(entry.nativeEntry().scrape_downloaded) : tr("N/A"));
+#if LIBTORRENT_VERSION_NUM >= 10200
+        struct ScrapeAccumulator {
+            ScrapeAccumulator(int libtorrent::announce_endpoint::* memb)
+                : m_empty {true}
+                , m_memb {memb}
+            {
+            }
+
+            int operator() (int a, const libtorrent::announce_endpoint& b)
+            {
+                if (b.*m_memb > 0) {
+                    m_empty = false;
+                    return a + b.*m_memb;
+                }
+                else {
+                    return a;
+                }
+            }
+
+            bool empty() const {
+                return m_empty;
+            }
+        private:
+            bool m_empty;
+            int libtorrent::announce_endpoint::* m_memb;
+        };
+
+        const auto endpoints = entry.nativeEntry().endpoints;
+        ScrapeAccumulator aCompleted {&libtorrent::announce_endpoint::scrape_complete};
+        const auto completed = std::accumulate(endpoints.begin(), endpoints.end(), 0, aCompleted);
+        item->setText(COL_SEEDS, aCompleted.empty() ? tr("N/A") : QString::number(completed));
+
+        ScrapeAccumulator aIncomplete {&libtorrent::announce_endpoint::scrape_incomplete};
+        const auto incomplete = std::accumulate(endpoints.begin(), endpoints.end(), 0, aIncomplete);
+        item->setText(COL_PEERS, aIncomplete.empty() ? tr("N/A") : QString::number(incomplete));
+
+        ScrapeAccumulator aDownloaded {&libtorrent::announce_endpoint::scrape_downloaded};
+        const auto downloaded = std::accumulate(endpoints.begin(), endpoints.end(), 0, aDownloaded);
+        item->setText(COL_DOWNLOADED, aDownloaded.empty() ? tr("N/A") : QString::number(downloaded));
+#elif LIBTORRENT_VERSION_NUM >= 10000
+        item->setText(COL_SEEDS, (entry.nativeEntry().scrape_complete) > 0 ? QString::number(entry.nativeEntry().scrape_complete) : tr("N/A"));
+        item->setText(COL_PEERS, (entry.nativeEntry().scrape_incomplete) > 0 ? QString::number(entry.nativeEntry().scrape_incomplete) : tr("N/A"));
+        item->setText(COL_DOWNLOADED, (entry.nativeEntry().scrape_downloaded) > 0 ? QString::number(entry.nativeEntry().scrape_downloaded) : tr("N/A"));
 #else
         item->setText(COL_SEEDS, tr("N/A"));
         item->setText(COL_PEERS, tr("N/A"));
diff --git a/src/gui/transferlistdelegate.cpp b/src/gui/transferlistdelegate.cpp
index 4500d1df4..d95efa929 100644
--- a/src/gui/transferlistdelegate.cpp
+++ b/src/gui/transferlistdelegate.cpp
@@ -82,7 +82,8 @@ void TransferListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
         break;
     case TransferListModel::TR_ETA: {
         opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
-        QItemDelegate::drawDisplay(painter, opt, option.rect, Utils::Misc::userFriendlyDuration(index.data().toLongLong()));
+        QItemDelegate::drawDisplay(painter, opt, option.rect,
+            Utils::Misc::userFriendlyDuration(index.data().value<std::chrono::seconds>()));
         break;
     }
     case TransferListModel::TR_SEEDS:
@@ -121,9 +122,9 @@ void TransferListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
         }
         break;
     case TransferListModel::TR_TIME_ELAPSED: {
-            const int elapsedTime = index.data().toInt();
-            const int seedingTime = index.data(Qt::UserRole).toInt();
-            const QString txt = (seedingTime > 0)
+            const auto elapsedTime = index.data().value<std::chrono::seconds>();
+            const auto seedingTime = index.data(Qt::UserRole).value<std::chrono::seconds>();
+            const QString txt = (seedingTime > seedingTime.zero())
                 ? tr("%1 (seeded for %2)", "e.g. 4m39s (seeded for 3m10s)")
                     .arg(Utils::Misc::userFriendlyDuration(elapsedTime)
                         , Utils::Misc::userFriendlyDuration(seedingTime))
@@ -176,15 +177,15 @@ void TransferListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
         }
         break;
     case TransferListModel::TR_LAST_ACTIVITY: {
-            qlonglong elapsed = index.data().toLongLong();
-            if (hideValues && ((elapsed < 0) || (elapsed >= MAX_ETA)))
+            auto elapsed = index.data().value<std::chrono::seconds>();
+            if (hideValues && ((elapsed < elapsed.zero()) || (elapsed >= MAX_ETA)))
                 break;
 
             // Show '< 1m ago' when elapsed time is 0
-            if (elapsed == 0)
-                elapsed = 1;
+            if (elapsed == elapsed.zero())
+                elapsed = std::chrono::seconds(1);
 
-            QString elapsedString = (elapsed >= 0)
+            QString elapsedString = (elapsed >= elapsed.zero())
                 ? tr("%1 ago", "e.g.: 1h 20m ago").arg(Utils::Misc::userFriendlyDuration(elapsed))
                 : Utils::Misc::userFriendlyDuration(elapsed);
 
diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp
index 3c5304257..efe605eee 100644
--- a/src/gui/transferlistmodel.cpp
+++ b/src/gui/transferlistmodel.cpp
@@ -194,7 +194,7 @@ QVariant TransferListModel::data(const QModelIndex &index, int role) const
     case TR_UPSPEED:
         return torrent->uploadPayloadRate();
     case TR_ETA:
-        return torrent->eta();
+        return QVariant::fromValue(torrent->eta());
     case TR_RATIO:
         return torrent->realRatio();
     case TR_CATEGORY:
@@ -225,7 +225,7 @@ QVariant TransferListModel::data(const QModelIndex &index, int role) const
     case TR_AMOUNT_LEFT:
         return torrent->incompletedSize();
     case TR_TIME_ELAPSED:
-        return (role == Qt::DisplayRole) ? torrent->activeTime() : torrent->seedingTime();
+        return QVariant::fromValue(role == Qt::DisplayRole ? torrent->activeTime() : torrent->seedingTime());
     case TR_SAVE_PATH:
         return Utils::Fs::toNativePath(torrent->savePath());
     case TR_COMPLETED:
@@ -237,7 +237,7 @@ QVariant TransferListModel::data(const QModelIndex &index, int role) const
     case TR_LAST_ACTIVITY:
         if (torrent->isPaused() || torrent->isChecking())
             return -1;
-        return torrent->timeSinceActivity();
+        return QVariant::fromValue(torrent->timeSinceActivity());
     case TR_TOTAL_SIZE:
         return torrent->totalSize();
     }
diff --git a/src/gui/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp
index 22a417d0c..1eb94b48f 100644
--- a/src/gui/transferlistsortmodel.cpp
+++ b/src/gui/transferlistsortmodel.cpp
@@ -166,8 +166,8 @@ bool TransferListSortModel::lessThan(const QModelIndex &left, const QModelIndex
 
             const qlonglong etaL = left.data().toLongLong();
             const qlonglong etaR = right.data().toLongLong();
-            const bool isInvalidL = ((etaL < 0) || (etaL >= MAX_ETA));
-            const bool isInvalidR = ((etaR < 0) || (etaR >= MAX_ETA));
+            const bool isInvalidL = ((etaL < 0) || (etaL >= MAX_ETA.count()));
+            const bool isInvalidR = ((etaR < 0) || (etaR >= MAX_ETA.count()));
             if (isInvalidL && isInvalidR) {
                 if (isSeedingL)  // Both seeding
                     return dateLessThan(TransferListModel::TR_SEED_DATE, left, right, true);
diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp
index 13349987c..eedecac2c 100644
--- a/src/gui/transferlistwidget.cpp
+++ b/src/gui/transferlistwidget.cpp
@@ -657,7 +657,7 @@ void TransferListWidget::setMaxRatioSelectedTorrents()
     if (torrents.count() == 1)
         currentMaxRatio = torrents[0]->maxRatio();
 
-    int currentMaxSeedingTime = BitTorrent::Session::instance()->globalMaxSeedingMinutes();
+    std::chrono::minutes currentMaxSeedingTime = BitTorrent::Session::instance()->globalMaxSeedingTime();
     if (torrents.count() == 1)
         currentMaxSeedingTime = torrents[0]->maxSeedingTime();
 
@@ -674,7 +674,7 @@ void TransferListWidget::setMaxRatioSelectedTorrents()
         qreal ratio = (dlg.useDefault() ? BitTorrent::TorrentHandle::USE_GLOBAL_RATIO : dlg.ratio());
         torrent->setRatioLimit(ratio);
 
-        int seedingTime = (dlg.useDefault() ? BitTorrent::TorrentHandle::USE_GLOBAL_SEEDING_TIME : dlg.seedingTime());
+        auto seedingTime = (dlg.useDefault() ? BitTorrent::TorrentHandle::USE_GLOBAL_SEEDING_TIME : dlg.seedingTime());
         torrent->setSeedingTimeLimit(seedingTime);
     }
 }
diff --git a/src/gui/updownratiodialog.cpp b/src/gui/updownratiodialog.cpp
index 4bd27bda4..6f0a90e16 100644
--- a/src/gui/updownratiodialog.cpp
+++ b/src/gui/updownratiodialog.cpp
@@ -31,13 +31,16 @@
 
 #include <QMessageBox>
 
+#include <boost/math/special_functions/relative_difference.hpp>
+
 #include "base/bittorrent/session.h"
+#include "base/bittorrent/torrenthandle.h"
 #include "ui_updownratiodialog.h"
 #include "utils.h"
 
 UpDownRatioDialog::UpDownRatioDialog(bool useDefault, qreal initialRatioValue,
-                               qreal maxRatioValue, int initialTimeValue,
-                               int maxTimeValue, QWidget *parent)
+                               qreal maxRatioValue, std::chrono::minutes initialTimeValue,
+                               std::chrono::minutes maxTimeValue, QWidget *parent)
     : QDialog(parent)
     , m_ui(new Ui::UpDownRatioDialog)
 {
@@ -46,10 +49,11 @@ UpDownRatioDialog::UpDownRatioDialog(bool useDefault, qreal initialRatioValue,
     if (useDefault) {
         m_ui->useDefaultButton->setChecked(true);
     }
-    else if ((initialRatioValue == -1.) && (initialTimeValue == -1)) {
+    else if ((boost::math::epsilon_difference(initialRatioValue, BitTorrent::TorrentHandle::NO_RATIO_LIMIT) < 1) &&
+        (initialTimeValue == BitTorrent::TorrentHandle::NO_SEEDING_TIME_LIMIT)) {
         m_ui->noLimitButton->setChecked(true);
         initialRatioValue = BitTorrent::Session::instance()->globalMaxRatio();
-        initialTimeValue = BitTorrent::Session::instance()->globalMaxSeedingMinutes();
+        initialTimeValue = BitTorrent::Session::instance()->globalMaxSeedingTime();
     }
     else {
         m_ui->torrentLimitButton->setChecked(true);
@@ -57,7 +61,7 @@ UpDownRatioDialog::UpDownRatioDialog(bool useDefault, qreal initialRatioValue,
         if (initialRatioValue >= 0)
             m_ui->checkMaxRatio->setChecked(true);
 
-        if (initialTimeValue >= 0)
+        if (initialTimeValue.count() >= 0)
             m_ui->checkMaxTime->setChecked(true);
     }
 
@@ -66,8 +70,8 @@ UpDownRatioDialog::UpDownRatioDialog(bool useDefault, qreal initialRatioValue,
     m_ui->ratioSpinBox->setValue(initialRatioValue);
 
     m_ui->timeSpinBox->setMinimum(0);
-    m_ui->timeSpinBox->setMaximum(maxTimeValue);
-    m_ui->timeSpinBox->setValue(initialTimeValue);
+    m_ui->timeSpinBox->setMaximum(maxTimeValue.count());
+    m_ui->timeSpinBox->setValue(initialTimeValue.count());
 
     connect(m_ui->buttonGroup, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked)
             , this, &UpDownRatioDialog::handleRatioTypeChanged);
@@ -98,9 +102,10 @@ qreal UpDownRatioDialog::ratio() const
     return (m_ui->noLimitButton->isChecked() || !m_ui->checkMaxRatio->isChecked()) ? -1. : m_ui->ratioSpinBox->value();
 }
 
-int UpDownRatioDialog::seedingTime() const
+std::chrono::minutes UpDownRatioDialog::seedingTime() const
 {
-    return (m_ui->noLimitButton->isChecked() || !m_ui->checkMaxTime->isChecked()) ? -1 : m_ui->timeSpinBox->value();
+    return (m_ui->noLimitButton->isChecked() || !m_ui->checkMaxTime->isChecked()) ?
+        BitTorrent::TorrentHandle::NO_SEEDING_TIME_LIMIT : std::chrono::minutes(m_ui->timeSpinBox->value());
 }
 
 void UpDownRatioDialog::handleRatioTypeChanged()
diff --git a/src/gui/updownratiodialog.h b/src/gui/updownratiodialog.h
index 725b5d99e..c97851d70 100644
--- a/src/gui/updownratiodialog.h
+++ b/src/gui/updownratiodialog.h
@@ -31,6 +31,8 @@
 
 #include <QDialog>
 
+#include <chrono>
+
 namespace Ui
 {
     class UpDownRatioDialog;
@@ -42,13 +44,13 @@ class UpDownRatioDialog : public QDialog
 
 public:
     UpDownRatioDialog(bool useDefault, qreal initialValue, qreal maxValue,
-            int initialTimeValue, int maxTimeValue,
+            std::chrono::minutes initialTimeValue, std::chrono::minutes maxTimeValue,
             QWidget *parent = nullptr);
     ~UpDownRatioDialog();
 
     bool useDefault() const;
     qreal ratio() const;
-    int seedingTime() const;
+    std::chrono::minutes seedingTime() const;
 
 public slots:
     void accept() override;
diff --git a/src/gui/updownratiodialog.ui b/src/gui/updownratiodialog.ui
index edfef4312..156932cf9 100644
--- a/src/gui/updownratiodialog.ui
+++ b/src/gui/updownratiodialog.ui
@@ -60,24 +60,18 @@
       </widget>
      </item>
      <item row="2" column="2">
-      <widget class="QDoubleSpinBox" name="timeSpinBox">
+      <widget class="QSpinBox" name="timeSpinBox">
        <property name="sizePolicy">
         <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
          <horstretch>0</horstretch>
          <verstretch>0</verstretch>
         </sizepolicy>
        </property>
-       <property name="decimals">
-        <number>0</number>
-       </property>
        <property name="maximum">
-        <double>525600.000000000000000</double>
-       </property>
-       <property name="singleStep">
-        <double>1.000000000000000</double>
+        <number>525600</number>
        </property>
        <property name="value">
-        <double>1440.000000000000000</double>
+        <number>1440</number>
        </property>
       </widget>
      </item>
diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp
index fad7bc23e..453592bc6 100644
--- a/src/webui/api/appcontroller.cpp
+++ b/src/webui/api/appcontroller.cpp
@@ -178,8 +178,8 @@ void AppController::preferencesAction()
     // Share Ratio Limiting
     data["max_ratio_enabled"] = (session->globalMaxRatio() >= 0.);
     data["max_ratio"] = session->globalMaxRatio();
-    data["max_seeding_time_enabled"] = (session->globalMaxSeedingMinutes() >= 0.);
-    data["max_seeding_time"] = session->globalMaxSeedingMinutes();
+    data["max_seeding_time_enabled"] = (session->globalMaxSeedingTime().count() >= 0);
+    data["max_seeding_time"] = static_cast<int>(session->globalMaxSeedingTime().count());
     data["max_ratio_act"] = session->maxRatioAction();
     // Add trackers
     data["add_trackers_enabled"] = session->isAddTrackersEnabled();
@@ -420,9 +420,9 @@ void AppController::setPreferencesAction()
     }
     if (m.contains("max_seeding_time_enabled")) {
         if (m["max_seeding_time_enabled"].toBool())
-            session->setGlobalMaxSeedingMinutes(m["max_seeding_time"].toInt());
+            session->setGlobalMaxSeedingTime(std::chrono::minutes(m["max_seeding_time"].toInt()));
         else
-            session->setGlobalMaxSeedingMinutes(-1);
+            session->setGlobalMaxSeedingTime(std::chrono::minutes(-1));
     }
     if (m.contains("max_ratio_act"))
         session->setMaxRatioAction(static_cast<MaxRatioAction>(m["max_ratio_act"].toInt()));
diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp
index c78f5b405..1f0b8a363 100644
--- a/src/webui/api/serialize/serialize_torrent.cpp
+++ b/src/webui/api/serialize/serialize_torrent.cpp
@@ -102,7 +102,7 @@ QVariantMap serialize(const BitTorrent::TorrentHandle &torrent)
     const qreal ratio = torrent.realRatio();
     ret[KEY_TORRENT_RATIO] = (ratio > BitTorrent::TorrentHandle::MAX_RATIO) ? -1 : ratio;
     ret[KEY_TORRENT_STATE] = torrentStateToString(torrent.state());
-    ret[KEY_TORRENT_ETA] = torrent.eta();
+    ret[KEY_TORRENT_ETA] = static_cast<qlonglong>(torrent.eta().count());
     ret[KEY_TORRENT_SEQUENTIAL_DOWNLOAD] = torrent.isSequentialDownload();
     if (torrent.hasMetadata())
         ret[KEY_TORRENT_FIRST_LAST_PIECE_PRIO] = torrent.hasFirstLastPiecePriority();
@@ -123,19 +123,19 @@ QVariantMap serialize(const BitTorrent::TorrentHandle &torrent)
     ret[KEY_TORRENT_AMOUNT_LEFT] = torrent.incompletedSize();
     ret[KEY_TORRENT_AMOUNT_COMPLETED] = torrent.completedSize();
     ret[KEY_TORRENT_MAX_RATIO] = torrent.maxRatio();
-    ret[KEY_TORRENT_MAX_SEEDING_TIME] = torrent.maxSeedingTime();
+    ret[KEY_TORRENT_MAX_SEEDING_TIME] = static_cast<qlonglong>(torrent.maxSeedingTime().count());
     ret[KEY_TORRENT_RATIO_LIMIT] = torrent.ratioLimit();
-    ret[KEY_TORRENT_SEEDING_TIME_LIMIT] = torrent.seedingTimeLimit();
+    ret[KEY_TORRENT_SEEDING_TIME_LIMIT] = static_cast<qlonglong>(torrent.seedingTimeLimit().count());
     ret[KEY_TORRENT_LAST_SEEN_COMPLETE_TIME] = torrent.lastSeenComplete().toTime_t();
     ret[KEY_TORRENT_AUTO_TORRENT_MANAGEMENT] = torrent.isAutoTMMEnabled();
-    ret[KEY_TORRENT_TIME_ACTIVE] = torrent.activeTime();
+    ret[KEY_TORRENT_TIME_ACTIVE] = static_cast<qlonglong>(torrent.activeTime().count());
 
     if (torrent.isPaused() || torrent.isChecking()) {
         ret[KEY_TORRENT_LAST_ACTIVITY_TIME] = 0;
     }
     else {
         QDateTime dt = QDateTime::currentDateTime();
-        dt = dt.addSecs(-torrent.timeSinceActivity());
+        dt = dt.addSecs(-torrent.timeSinceActivity().count());
         ret[KEY_TORRENT_LAST_ACTIVITY_TIME] = dt.toTime_t();
     }
 
diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp
index ac2b1c028..7a59a489b 100644
--- a/src/webui/api/torrentscontroller.cpp
+++ b/src/webui/api/torrentscontroller.cpp
@@ -234,6 +234,7 @@ void TorrentsController::infoAction()
 //   - "comment": Torrent comment
 void TorrentsController::propertiesAction()
 {
+    using std::chrono::duration_cast;
     checkParams({"hash"});
 
     const QString hash {params()["hash"]};
@@ -242,9 +243,9 @@ void TorrentsController::propertiesAction()
     if (!torrent)
         throw APIError(APIErrorType::NotFound);
 
-    dataDict[KEY_PROP_TIME_ELAPSED] = torrent->activeTime();
-    dataDict[KEY_PROP_SEEDING_TIME] = torrent->seedingTime();
-    dataDict[KEY_PROP_ETA] = torrent->eta();
+    dataDict[KEY_PROP_TIME_ELAPSED] = static_cast<qlonglong>(torrent->activeTime().count());
+    dataDict[KEY_PROP_SEEDING_TIME] = static_cast<qlonglong>(torrent->seedingTime().count());
+    dataDict[KEY_PROP_ETA] = static_cast<qlonglong>(torrent->eta().count());
     dataDict[KEY_PROP_CONNECT_COUNT] = torrent->connectionsCount();
     dataDict[KEY_PROP_CONNECT_COUNT_LIMIT] = torrent->connectionsLimit();
     dataDict[KEY_PROP_DOWNLOADED] = torrent->totalDownload();
@@ -252,9 +253,11 @@ void TorrentsController::propertiesAction()
     dataDict[KEY_PROP_UPLOADED] = torrent->totalUpload();
     dataDict[KEY_PROP_UPLOADED_SESSION] = torrent->totalPayloadUpload();
     dataDict[KEY_PROP_DL_SPEED] = torrent->downloadPayloadRate();
-    dataDict[KEY_PROP_DL_SPEED_AVG] = torrent->totalDownload() / (1 + torrent->activeTime() - torrent->finishedTime());
+    dataDict[KEY_PROP_DL_SPEED_AVG] = torrent->totalDownload() /
+        (1 + duration_cast<std::chrono::seconds>(torrent->activeTime() - torrent->finishedTime()).count());
     dataDict[KEY_PROP_UP_SPEED] = torrent->uploadPayloadRate();
-    dataDict[KEY_PROP_UP_SPEED_AVG] = torrent->totalUpload() / (1 + torrent->activeTime());
+    dataDict[KEY_PROP_UP_SPEED_AVG] = torrent->totalUpload() /
+        (1 + duration_cast<std::chrono::seconds>(torrent->activeTime()).count());
     dataDict[KEY_PROP_DL_LIMIT] = torrent->downloadLimit() <= 0 ? -1 : torrent->downloadLimit();
     dataDict[KEY_PROP_UP_LIMIT] = torrent->uploadLimit() <= 0 ? -1 : torrent->uploadLimit();
     dataDict[KEY_PROP_WASTED] = torrent->wastedSize();
@@ -264,7 +267,7 @@ void TorrentsController::propertiesAction()
     dataDict[KEY_PROP_PEERS_TOTAL] = torrent->totalLeechersCount();
     const qreal ratio = torrent->realRatio();
     dataDict[KEY_PROP_RATIO] = ratio > BitTorrent::TorrentHandle::MAX_RATIO ? -1 : ratio;
-    dataDict[KEY_PROP_REANNOUNCE] = torrent->nextAnnounce();
+    dataDict[KEY_PROP_REANNOUNCE] = static_cast<qlonglong>(torrent->nextAnnounce().count());
     dataDict[KEY_PROP_TOTAL_SIZE] = torrent->totalSize();
     dataDict[KEY_PROP_PIECES_NUM] = torrent->piecesCount();
     dataDict[KEY_PROP_PIECE_SIZE] = torrent->pieceLength();
@@ -636,7 +639,7 @@ void TorrentsController::setShareLimitsAction()
     applyToTorrents(hashes, [ratioLimit, seedingTimeLimit](BitTorrent::TorrentHandle *torrent)
     {
         torrent->setRatioLimit(ratioLimit);
-        torrent->setSeedingTimeLimit(seedingTimeLimit);
+        torrent->setSeedingTimeLimit(std::chrono::minutes(seedingTimeLimit));
     });
 }
 
