qbt-clean 0.149.0

Automated rules-based cleaning of qBittorrent torrents.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# qbt-clean

A tool for cleaning up torrents in qBittorrent. The defining feature is that it attempts to identify groups of torrents that are the same content. For example torrents that are likely hard-linked or ref-linked to the same underlying data.

> [!warning]
> This is a tool to delete data, it will do so. It is highly recommended to run in dry-run (the default) first and validate that it is deleting what you expect.
>
> Additionally this tool is very new and not particularly well tested. Additionally the configuration is constantly changing at these early stages.
>
> **USE AT YOUR OWN RISK**

## Commands

### simulate

Simulate is equivalent to `clean` except that it doesn't actually modify anything. Use it to see what would be cleaned.

```sh
qbt-clean --config=config.scfg simulate
```

### clean

Clean torrents until space target is reached. Unpinned groups will be cleaned from highest score to lowest until the free space target is met.

```sh
qbt-clean --config=config.scfg clean
```

### group

Show groups as well as info about them such as if they are pinned or not and what rank they are in the cleaning order. Use `--help` for more options.

```sh
qbt-clean --config=config.scfg group
```

## Grouping

The current grouping is incredibly simple. It just sums up the size of substantial files in each torrent. Any files that share the exact same total size are considered a group.

## Config

The configuration is read from a file passed via `--config <path>` (in [scfg](https://crates.io/crates/simple_config) format) or `--json <path>` (in JSON format). Exactly one must be provided. This documentation displays scfg format.

An example simple config looks like this:

```yaml
# Operational Config
qbt-url: https://admin:yohn0daeV6eeth4bieyahqu1ogohMaiz@qbttorrent.example
target-free: 0.8 TiB

# Requirements
last-activity-min: 7d
seed-time-min: 30d

# Ranking
seeder-count-weight: 1

categories-allowed:
	cross-seed
	other
```

### Operational Config

#### `target-free`

Clean up torrents until this much space is free on the filesystem.

Note: This assumes that each deleted group frees up the amount of the space of the torrent size. This can underestimate (if the backing blocks are still used by other files) or overestimate (if the torrents in the group did not share data).

```yaml
target-free: 100 GiB
```

#### `qbt-url`

The URL to interact with qBittorrent at. This should also contain the username and password for authentication.

```yaml
qbt-url: http://localhost:8080
```

#### `qbt-headers`

These headers will be passed to qBittorrent on every HTTP request. This is most useful for proxy forward authentication. For example basic auth or other credentials can be passed.

```yaml
qbt-headers:
	Authorization: Basic QXp1cmVEaWFtb25kOmh1bnRlcjI=
```

#### `parallelism`

The parallelism to use. This roughly corresponds to the number of concurrent requests to qBittorrent.

The default is currently 16 which strikes a balance between performing reasonably in high-latency environments and not unnecessarily loading qBittorrent. Local users can probably use a much lower number, especially if total execution time isn't very important.

### Requirements

These requirements prevent cleaning up torrents that shouldn't be. They are applied to each torrent in a group individually. Any torrent in a group that fails to satisfy a requirement will prevent the entire group from being removed.

The default values are being shown.

#### `categories-allowed`

The torrent must be in one of these categories to be cleaned up.

There are no defaults (meaning that if this isn't set no torrents will match).

#### `last-activity-min`

If the torrent was active within this period it won't be cleaned up.

```yaml
last-activity-min: 7d
```

#### `seed-time-min`

If the total torrent seed time is less than this it won't be cleaned up.

```yaml
seed-time-min: 14d
```

#### `seeder-count-min`

If a torrent has less than this many seeders it won't be cleaned up.

```yaml
seeder-count-min: 3
```

#### `state-allowed`

If the torrent isn't in one of these states it won't be cleaned up. These can be specified with either the identifiers that qBittorrent uses or more consistent kebab-case versions.

```yaml
state-allowed:
	error
	missing-files
	stopped-downloading
	stopped-uploading
	uploading-forced
	uploading-queued
	uploading-stalled
```

If this field is empty, then any state is allowed.

### Rules

Rules allow you to override the requirements for specific torrents. For example the following rule will avoid cleaning up Archive.org torrents unless they have at least a year of seed time. Note that this applies only to the specific torrent. Other torrents in the group may have less seed time when cleaned up.

```yaml
rules:
	:
		match:
			tracker-url:
				http://bt\d+.archive.org:6969/announce
		seed-time-min: 365d
```

Rules consist of a `match:` block which contains filters and then a set of requirements to override when the filters match. Later overrides supersede earlier overrides.

#### Effects

##### Global Requirements

All global requirements can be modified with an attribute of the same name.

##### `pin`

There is a special `pin` requirement that overrides any other requirement. So a filter that applies `pin: true` will prevent a torrent (and it's group) from ever being deleted. `pin: false` will prevent a torrent from ever holding a group alive (but other torrents in the group may keep it alive). Just like other requirements only the **last** `pin` rule that matches applies.

> [!warning]
> Be very careful with `pin: false`. It overrides **all** requirements and is very easy to delete more than you intended. For example do you have an "Important Never Delete" category? It won't be protected by the `categories-allowed` requirement anymore. So be very careful that all rules that apply `pin: false` are very particular about what you want to never pin.

##### `pin-reason`

An optional human-readable string that explains why a `pin` rule applies. This value is purely informational to help you understand which rules are affecting the cleaning decisions.

```yaml
rules:
	:
		match:
			categories:
				trash
		pin: false
		pin-reason: Trash category, always cleanable.
```

##### `include-in-score`

This value is a boolean that allows excluding torrents from the group score. This is commonly used with `pin: false`. Additionally consider configuring the `no-scored-torrents-weight` to influence the score of groups containing only `include-in-score: false` torrents.

#### Matchers

##### any

The `any` filter takes a list of filters and matches if any of them match.

This example will set the minimum seeder count if either a specific tracker is on the torrent or it is in the `favourites` category.

```yaml
rules:
	:
		match:
			any:
				:
					tracker-url:
						https://tracker.example
				:
					categories:
						favourites
			seeder-count-min: 10
```

##### categories

The `categories` filter matches torrents that are in the given categories.

```yaml
rules:
	match:
		categories:
			Favourites
			Keep Forever
```

Note: There is also a `categories-allowed` requirement.

##### is-private

Match based on if the torrent is marked as private or not.

```yaml
rules:
	:
		match:
			is-private: true
```

##### last-activity

Match based on when the torrent was last active.

```yaml
rules:
	:
		match:
			last-activity:
				lt: 1d
```

##### leech-count

Match the number of leechers.

```yaml
rules:
	match:
		leech-count:
			gt: 10
```

##### name

Match any torrents who's name matches the regex.

```yaml
rules:
	:
		match:
			name:
				Arch.*\.iso
```

##### none

Match if none of the given filters match:

```yaml
rules:
	match:
		# Match if not (seed-count and leech-count less than 10) AND not name matches the regex.
		none:
			:
				seed-count:
					lt: 10
				leech-count:
					lt: 10
			:
				name:
					Arch.*\.iso
```

##### seed-time

Match based on the seed time for the torrent.

```yaml
rules:
	:
		match:
			seed-time:
				gt: 1h
```

##### ratio

Match based on the ratio of the torrent.

```yaml
rules:
	match:
		ratio:
			ge: 8
```

##### seed-count

Match the number of seeders.

```yaml
rules:
	match:
		seed-count:
			ge: 10
```

##### state

Match if the torrent state is in any of the listed states.

```yaml
rules:
	:
		match:
			state:
				stopped-downloading
				uploading-forced
```

##### tracker-msg

Match if any of the tracker's tracker messages matches any of the given regexes.

```
rules:
	:
		match:
			tracker-msg:
				[Uu]nregistered torrent
				Torrent has been deleted.
```

##### tracker-status

Match if any of the tracker's trackers' statuses matches any of the given states.

```
rules:
	:
		match:
			tracker-status:
				tracker-error
				unreachable
```

Supported statuses:

- `disabled`
- `not-contacted`
- `not-working`
- `other`
- `tracker-error`
- `unreachable`
- `updating`
- `working`

> [!warning]
> Setting this can make your cleaning slightly unpredictable as tracker status regularly changes (notably to and from `updating`).

##### tracker-url

Match if any of the tracker URLs match any of the given regexes.

```
rules:
	:
		match:
			tracker-url:
				http://bt\d+.archive.org:6969/announce
```

### Ranking

Ranking determines which torrents to delete first. The highest ranked torrents will be deleted until the desired space is reclaimed.

All ranking parameters take a floating point number and default to zero. If all ranking parameters are zero `seeder-count-weight: 1.0` will be applied.

#### `age-d-max-weight`

Rank based on the age of the oldest torrent in the group.

Note: If you want to prefer keeping older torrents the weight should be negative.

#### `age-d-min-weight`

Rank based on the age of the newest torrent in the group.

Note: If you want to prefer keeping older torrents the weight should be negative.

#### `copies-weight`

Rank based on number of torrents in the group.

Note: If you want to prefer keeping larger groups the weight should be negative.

#### `last-activity-d-weight`

Rank based on the last activity in the group.

#### `no-scored-torrents-weight`

If a group has no scored torrents, apply this weight. (See [`include-in-score`](#include-in-score))

#### `seeder-count-weight`

Rank based on total seeder count.

#### `size-weight`

Rank based on the size expected to be freed by deleting.

Note: This is roughly the size of a torrent in the group.