Skip to content

Commit 465d9a1

Browse files
committed
feat: add API token authentication support for qBittorrent v5.2.0+
1 parent c55b43c commit 465d9a1

6 files changed

Lines changed: 29 additions & 16 deletions

File tree

autoremovetorrents/client/deluge.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __init__(self, host):
2424
self._last_refresh = 0
2525

2626
# Login to Deluge
27-
def login(self, username, password):
27+
def login(self, username, password, api_token=None):
2828
# Split IP(or domain name) and port
2929
splits = self._host.split(':')
3030
host = splits[0] if len(splits) > 0 else ''

autoremovetorrents/client/qbittorrent.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ def client_version(self):
3636
return self._session.get(self._host+'/version/qbittorrent')
3737

3838
# Login
39-
def login(self, username, password):
39+
def login(self, username, password, api_token=None):
40+
if api_token:
41+
raise IncompatibleAPIVersion('API token authentication is only supported on qBittorrent v5.2.0+ with WebAPI v2.14.1+ (API v2)')
4042
return self._session.post(self._host+'/login', data={'username':username, 'password':password})
4143

4244
# Get server state
@@ -89,8 +91,15 @@ def client_version(self):
8991
return self._session.get(self._host+'/api/v2/app/version')
9092

9193
# Login
92-
def login(self, username, password):
93-
return self._session.post(self._host+'/api/v2/auth/login', data={'username':username, 'password':password})
94+
def login(self, username, password, api_token=None):
95+
if api_token:
96+
self._session.headers.update({
97+
'Authorization': f'Bearer {api_token}'
98+
})
99+
# Verify the token works by calling a simple API endpoint
100+
return self.server_state()
101+
else:
102+
return self._session.post(self._host+'/api/v2/auth/login', data={'username':username, 'password':password})
94103

95104
# Get server state
96105
def server_state(self):
@@ -136,15 +145,17 @@ def __init__(self, host):
136145
raise IncompatibleAPIVersion('Incompatible qbittorrent client. The current API version may be unsupported.')
137146

138147
# Login to qBittorrent
139-
def login(self, username, password):
148+
def login(self, username, password, api_token=None):
140149
try:
141-
request = self._request_handler.login(username, password)
150+
request = self._request_handler.login(username, password, api_token)
142151
except Exception as exc:
143152
raise ConnectionFailure(str(exc))
144153

145154
if request.status_code == 200:
146-
if request.text == 'Fails.': # Fail
147-
raise LoginFailure(request.text)
155+
# Check if this is a response from the login endpoint (plain text) or from server_state() (JSON)
156+
if not api_token: # Only check "Fails." for username/password login
157+
if request.text == 'Fails.':
158+
raise LoginFailure(request.text)
148159
else:
149160
raise LoginFailure('The server returned HTTP %d.' % request.status_code)
150161

autoremovetorrents/client/transmission.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def __init__(self, host):
2222
self._session = requests.Session()
2323

2424
# Login to Transmission
25-
def login(self, username, password):
25+
def login(self, username, password, api_token=None):
2626
# Save authentication of session
2727
self._session.auth = (username, password)
2828

autoremovetorrents/client/utorrent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def __init__(self, host):
2626
self._refresh_time = 0
2727

2828
# Login to uTorrent
29-
def login(self, username, password):
29+
def login(self, username, password, api_token=None):
3030
# HTTP Authorization
3131
self._session.auth = (username, password)
3232
# Requests Token

autoremovetorrents/task.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def __init__(self, name, conf, remove_torrents = True):
2121

2222
# Replace environment variables first
2323
pattern = re.compile(r'\$\(([^\)]+)\)')
24-
replace_keys = ['host', 'username', 'password']
24+
replace_keys = ['host', 'username', 'password', 'api_token']
2525
for key in replace_keys:
2626
if key in conf:
2727
env = pattern.match(str(conf[key]))
@@ -34,6 +34,7 @@ def __init__(self, name, conf, remove_torrents = True):
3434
self._host = conf['host'].rstrip('/')
3535
self._username = conf['username'] if 'username' in conf else ''
3636
self._password = conf['password'] if 'password' in conf else ''
37+
self._api_token = conf['api_token'] if 'api_token' in conf else ''
3738
self._enabled_remove = remove_torrents
3839
self._delete_data = conf['delete_data'] if 'delete_data' in conf else False
3940
self._strategies = conf['strategies'] if 'strategies' in conf else []
@@ -55,8 +56,8 @@ def __init__(self, name, conf, remove_torrents = True):
5556

5657
# Print debug logs
5758
self._logger.debug("Configuration of task '%s':" % self._name)
58-
self._logger.debug('Client: %s, Host: %s, Username: %s, Password: %s' % (
59-
self._client_name, self._host, self._username, self._password
59+
self._logger.debug('Client: %s, Host: %s, Username: %s, Password: %s, API Token: %s' % (
60+
self._client_name, self._host, self._username, self._password, self._api_token
6061
))
6162
self._logger.debug('Remove Torrents: %s, Remove Torrents and Data: %s' % (
6263
self._enabled_remove, self._delete_data
@@ -83,7 +84,7 @@ def _login(self):
8384

8485
# Login
8586
self._logger.info('Logging in...')
86-
self._client.login(self._username, self._password)
87+
self._client.login(self._username, self._password, self._api_token)
8788
self._logger.info('Login successfully. The client is %s.' % self._client.version())
8889
self._logger.info('WebUI API version: %s' % self._client.api_version())
8990

docs/config.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ For qBittorrent/Transmission/μTorrent, this program works with your client's We
6767

6868
* ``client``: Your client name. It's case-insensitive.
6969
* ``host``: The URL of your client's WebUI, and the URL must have a scheme (http:// or https://).
70-
* ``username``: The username of the WebUI.
71-
* ``password``: The password of the WebUI.
70+
* ``username``: The username of the WebUI (optional for qBittorrent if using API token).
71+
* ``password``: The password of the WebUI (optional for qBittorrent if using API token).
72+
* ``api_token``: The API key for qBittorrent v5.2.0+ with WebAPI v2.14.1+ (optional, if provided, username/password can be omitted).
7273

7374
For Deluge
7475
+++++++++++

0 commit comments

Comments
 (0)