qBitrr2 5.6.0__tar.gz → 5.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. qbitrr2-5.6.2/PKG-INFO +260 -0
  2. qbitrr2-5.6.2/README.md +156 -0
  3. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/pyproject.toml +1 -1
  4. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/arss.py +153 -1
  5. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/bundled_data.py +2 -2
  6. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/db_lock.py +24 -10
  7. qbitrr2-5.6.2/qBitrr/static/assets/app.css +1 -0
  8. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/app.js +5 -5
  9. qbitrr2-5.6.2/qBitrr/static/assets/app.js.map +1 -0
  10. qbitrr2-5.6.2/qBitrr2.egg-info/PKG-INFO +260 -0
  11. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/setup.cfg +1 -1
  12. qbitrr2-5.6.0/PKG-INFO +0 -1210
  13. qbitrr2-5.6.0/README.md +0 -1106
  14. qbitrr2-5.6.0/qBitrr/static/assets/app.css +0 -1
  15. qbitrr2-5.6.0/qBitrr/static/assets/app.js.map +0 -1
  16. qbitrr2-5.6.0/qBitrr2.egg-info/PKG-INFO +0 -1210
  17. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/LICENSE +0 -0
  18. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/MANIFEST.in +0 -0
  19. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/config.example.toml +0 -0
  20. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/__init__.py +0 -0
  21. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/auto_update.py +0 -0
  22. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/config.py +0 -0
  23. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/config_version.py +0 -0
  24. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/db_recovery.py +0 -0
  25. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/env_config.py +0 -0
  26. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/errors.py +0 -0
  27. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/ffprobe.py +0 -0
  28. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/gen_config.py +0 -0
  29. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/home_path.py +0 -0
  30. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/logger.py +0 -0
  31. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/main.py +0 -0
  32. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/search_activity_store.py +0 -0
  33. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/ArrView.js +0 -0
  34. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/ArrView.js.map +0 -0
  35. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/ConfigView.js +0 -0
  36. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/ConfigView.js.map +0 -0
  37. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/LogsView.js +0 -0
  38. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/LogsView.js.map +0 -0
  39. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/ProcessesView.js +0 -0
  40. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/ProcessesView.js.map +0 -0
  41. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/build.svg +0 -0
  42. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/check-mark.svg +0 -0
  43. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/close.svg +0 -0
  44. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/download.svg +0 -0
  45. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/gear.svg +0 -0
  46. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/lidarr.svg +0 -0
  47. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/live-streaming.svg +0 -0
  48. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/log.svg +0 -0
  49. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/logo.svg +0 -0
  50. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/plus.svg +0 -0
  51. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/process.svg +0 -0
  52. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/react-select.esm.js +0 -0
  53. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/react-select.esm.js.map +0 -0
  54. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/refresh-arrow.svg +0 -0
  55. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/table.js +0 -0
  56. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/table.js.map +0 -0
  57. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/trash.svg +0 -0
  58. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/up-arrow.svg +0 -0
  59. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/useInterval.js +0 -0
  60. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/useInterval.js.map +0 -0
  61. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/vendor.js +0 -0
  62. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/vendor.js.map +0 -0
  63. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/assets/visibility.svg +0 -0
  64. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/favicon-16x16.png +0 -0
  65. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/favicon-32x32.png +0 -0
  66. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/favicon-48x48.png +0 -0
  67. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/favicon.ico +0 -0
  68. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/icon-192.png +0 -0
  69. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/icon-512.png +0 -0
  70. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/index.html +0 -0
  71. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/logov2-clean.png +0 -0
  72. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/logov2-clean.svg +0 -0
  73. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/manifest.json +0 -0
  74. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/sw.js +0 -0
  75. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/static/vite.svg +0 -0
  76. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/tables.py +0 -0
  77. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/utils.py +0 -0
  78. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/versioning.py +0 -0
  79. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr/webui.py +0 -0
  80. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr2.egg-info/SOURCES.txt +0 -0
  81. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr2.egg-info/dependency_links.txt +0 -0
  82. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr2.egg-info/entry_points.txt +0 -0
  83. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr2.egg-info/requires.txt +0 -0
  84. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/qBitrr2.egg-info/top_level.txt +0 -0
  85. {qbitrr2-5.6.0 → qbitrr2-5.6.2}/setup.py +0 -0
qbitrr2-5.6.2/PKG-INFO ADDED
@@ -0,0 +1,260 @@
1
+ Metadata-Version: 2.4
2
+ Name: qBitrr2
3
+ Version: 5.6.2
4
+ Summary: Intelligent automation for qBittorrent and *Arr apps (Radarr/Sonarr/Lidarr) - health monitoring, instant imports, quality upgrades, request integration
5
+ Home-page: https://github.com/Feramance/qBitrr
6
+ Author: Feramance
7
+ Author-email: fera@fera.wtf
8
+ License: MIT
9
+ Project-URL: Homepage, https://github.com/Feramance/qBitrr
10
+ Project-URL: Documentation, https://feramance.github.io/qBitrr/
11
+ Project-URL: Issue Tracker, https://github.com/Feramance/qBitrr/issues
12
+ Project-URL: Source Code, https://github.com/Feramance/qBitrr
13
+ Project-URL: Changelog, https://github.com/Feramance/qBitrr/blob/master/CHANGELOG.md
14
+ Project-URL: Docker Hub, https://hub.docker.com/r/feramance/qbitrr
15
+ Project-URL: PyPI, https://pypi.org/project/qBitrr2/
16
+ Project-URL: Systemd Guide, https://feramance.github.io/qBitrr/getting-started/installation/systemd/
17
+ Keywords: qbittorrent,radarr,sonarr,lidarr,arr,automation,torrent,media,plex,jellyfin,overseerr,ombi
18
+ Classifier: Development Status :: 5 - Production/Stable
19
+ Classifier: Intended Audience :: Developers
20
+ Classifier: Intended Audience :: End Users/Desktop
21
+ Classifier: Intended Audience :: System Administrators
22
+ Classifier: License :: OSI Approved :: MIT License
23
+ Classifier: Natural Language :: English
24
+ Classifier: Operating System :: MacOS :: MacOS X
25
+ Classifier: Operating System :: Microsoft :: Windows
26
+ Classifier: Operating System :: POSIX :: Linux
27
+ Classifier: Programming Language :: Python :: 3 :: Only
28
+ Classifier: Programming Language :: Python :: 3.11
29
+ Classifier: Programming Language :: Python :: 3.12
30
+ Classifier: Programming Language :: Python :: Implementation :: CPython
31
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
32
+ Classifier: Topic :: Communications
33
+ Classifier: Topic :: Internet
34
+ Classifier: Topic :: Multimedia :: Video
35
+ Classifier: Topic :: System :: Monitoring
36
+ Classifier: Topic :: Terminals
37
+ Classifier: Topic :: Utilities
38
+ Classifier: Typing :: Typed
39
+ Requires-Python: <4,>=3.11
40
+ Description-Content-Type: text/markdown
41
+ License-File: LICENSE
42
+ Requires-Dist: cachetools
43
+ Requires-Dist: colorama
44
+ Requires-Dist: coloredlogs
45
+ Requires-Dist: flask
46
+ Requires-Dist: environ-config
47
+ Requires-Dist: ffmpeg-python
48
+ Requires-Dist: jaraco.docker
49
+ Requires-Dist: packaging
50
+ Requires-Dist: pathos
51
+ Requires-Dist: peewee
52
+ Requires-Dist: ping3
53
+ Requires-Dist: pyarr
54
+ Requires-Dist: qbittorrent-api
55
+ Requires-Dist: requests
56
+ Requires-Dist: tomlkit
57
+ Requires-Dist: waitress
58
+ Requires-Dist: croniter
59
+ Provides-Extra: dev
60
+ Requires-Dist: black==24.3.0; extra == "dev"
61
+ Requires-Dist: bump2version==1.0.1; extra == "dev"
62
+ Requires-Dist: isort==5.10.1; extra == "dev"
63
+ Requires-Dist: pip-tools==7.3.0; extra == "dev"
64
+ Requires-Dist: pre-commit==3.3.3; extra == "dev"
65
+ Requires-Dist: pyinstaller==5.13.1; extra == "dev"
66
+ Requires-Dist: pyupgrade==2.31.0; extra == "dev"
67
+ Requires-Dist: twine==3.7.1; extra == "dev"
68
+ Requires-Dist: ujson==5.10.0; extra == "dev"
69
+ Requires-Dist: upgrade-pip==0.1.4; extra == "dev"
70
+ Provides-Extra: fast
71
+ Requires-Dist: ujson==5.10.0; extra == "fast"
72
+ Provides-Extra: docs
73
+ Requires-Dist: mkdocs>=1.5.3; extra == "docs"
74
+ Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
75
+ Requires-Dist: mkdocs-material-extensions>=1.3.0; extra == "docs"
76
+ Requires-Dist: mkdocs-git-revision-date-localized-plugin>=1.2.0; extra == "docs"
77
+ Requires-Dist: mkdocs-minify-plugin>=0.7.0; extra == "docs"
78
+ Requires-Dist: mkdocs-redirects>=1.2.0; extra == "docs"
79
+ Requires-Dist: mkdocs-include-markdown-plugin>=6.0.0; extra == "docs"
80
+ Requires-Dist: pymdown-extensions>=10.0.0; extra == "docs"
81
+ Requires-Dist: markdown-include>=0.8.0; extra == "docs"
82
+ Provides-Extra: all
83
+ Requires-Dist: black==24.3.0; extra == "all"
84
+ Requires-Dist: bump2version==1.0.1; extra == "all"
85
+ Requires-Dist: isort==5.10.1; extra == "all"
86
+ Requires-Dist: pip-tools==7.3.0; extra == "all"
87
+ Requires-Dist: pre-commit==3.3.3; extra == "all"
88
+ Requires-Dist: pyinstaller==5.13.1; extra == "all"
89
+ Requires-Dist: pyupgrade==2.31.0; extra == "all"
90
+ Requires-Dist: twine==3.7.1; extra == "all"
91
+ Requires-Dist: ujson==5.10.0; extra == "all"
92
+ Requires-Dist: upgrade-pip==0.1.4; extra == "all"
93
+ Requires-Dist: ujson==5.10.0; extra == "all"
94
+ Requires-Dist: mkdocs>=1.5.3; extra == "all"
95
+ Requires-Dist: mkdocs-material>=9.5.0; extra == "all"
96
+ Requires-Dist: mkdocs-material-extensions>=1.3.0; extra == "all"
97
+ Requires-Dist: mkdocs-git-revision-date-localized-plugin>=1.2.0; extra == "all"
98
+ Requires-Dist: mkdocs-minify-plugin>=0.7.0; extra == "all"
99
+ Requires-Dist: mkdocs-redirects>=1.2.0; extra == "all"
100
+ Requires-Dist: mkdocs-include-markdown-plugin>=6.0.0; extra == "all"
101
+ Requires-Dist: pymdown-extensions>=10.0.0; extra == "all"
102
+ Requires-Dist: markdown-include>=0.8.0; extra == "all"
103
+ Dynamic: license-file
104
+
105
+ # <img src="assets/logov2-clean.png" alt="qBitrr Logo" width="40" style="vertical-align: middle;"/> qBitrr
106
+
107
+ [![PyPI](https://img.shields.io/pypi/v/qBitrr2?label=PyPI)](https://pypi.org/project/qBitrr2/)
108
+ [![Downloads](https://img.shields.io/pypi/dm/qBitrr2)](https://pypi.org/project/qBitrr2/)
109
+ [![Docker Pulls](https://img.shields.io/docker/pulls/feramance/qbitrr.svg)](https://hub.docker.com/r/feramance/qbitrr)
110
+ [![CodeQL](https://github.com/Feramance/qBitrr/actions/workflows/codeql.yml/badge.svg?branch=master)](https://github.com/Feramance/qBitrr/actions/workflows/codeql.yml)
111
+ [![Nightly Build](https://github.com/Feramance/qBitrr/actions/workflows/nightly.yml/badge.svg?branch=master)](https://github.com/Feramance/qBitrr/actions/workflows/nightly.yml)
112
+ [![pre-commit.ci](https://results.pre-commit.ci/badge/github/Feramance/qBitrr/master.svg)](https://results.pre-commit.ci/latest/github/Feramance/qBitrr/master)
113
+ [![License: MIT](https://img.shields.io/pypi/l/qbitrr)](LICENSE)
114
+
115
+ > 🧩 The intelligent glue between qBittorrent and the *Arr ecosystem (Radarr, Sonarr, Lidarr). Monitors torrent health, triggers instant imports, automates quality upgrades, manages disk space, integrates with request systems (Overseerr/Ombi), and provides a modern React dashboard for complete visibility and control.
116
+
117
+ ## 📚 Documentation
118
+
119
+ **Full documentation is available at: https://feramance.github.io/qBitrr/**
120
+
121
+ - [Getting Started](https://feramance.github.io/qBitrr/getting-started/) – Installation guides for pip, Docker, and native setups
122
+ - [Configuration](https://feramance.github.io/qBitrr/configuration/) – qBittorrent, Arr instances, quality profiles, and more
123
+ - [Features](https://feramance.github.io/qBitrr/features/) – Health monitoring, automated search, quality management, disk space, auto-updates
124
+ - [WebUI](https://feramance.github.io/qBitrr/webui/) – Built-in React dashboard with live monitoring and config editor
125
+ - [Troubleshooting](https://feramance.github.io/qBitrr/troubleshooting/) – Common issues and debug logging
126
+ - [API Reference](https://feramance.github.io/qBitrr/reference/api/) – REST API documentation
127
+
128
+ ## ⚡ Quick Start
129
+
130
+ ### 🐍 Install with pip
131
+ ```bash
132
+ python -m venv .venv
133
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
134
+ pip install qBitrr2
135
+
136
+ # First run creates ~/config/config.toml
137
+ qbitrr
138
+ ```
139
+
140
+ ### 🐳 Run with Docker
141
+ ```bash
142
+ docker run -d \
143
+ --name qbitrr \
144
+ --tty \
145
+ -e TZ=Europe/London \
146
+ -p 6969:6969 \
147
+ -v /path/to/appdata/qbitrr:/config \
148
+ -v /path/to/completed/downloads:/completed_downloads:rw \
149
+ --restart unless-stopped \
150
+ feramance/qbitrr:latest
151
+ ```
152
+
153
+ **Docker Compose:**
154
+ ```yaml
155
+ services:
156
+ qbitrr:
157
+ image: feramance/qbitrr:latest
158
+ container_name: qbitrr
159
+ restart: unless-stopped
160
+ tty: true
161
+ environment:
162
+ TZ: Europe/London
163
+ ports:
164
+ - "6969:6969"
165
+ volumes:
166
+ - /path/to/appdata/qbitrr:/config
167
+ - /path/to/completed/downloads:/completed_downloads:rw
168
+ ```
169
+
170
+ Access the WebUI at `http://<host>:6969/ui` after startup.
171
+
172
+ ## ✨ Key Features
173
+
174
+ - **🚑 Torrent Health Monitoring** – Detect stalled/failed downloads, auto-blacklist, trigger re-searches
175
+ - **🔍 Automated Search** – Missing media, quality upgrades, custom format scoring
176
+ - **🎯 Request Integration** – Pull requests from Overseerr/Ombi, prioritize user-requested media
177
+ - **📊 Quality Management** – RSS sync, queue refresh, profile switching, custom format enforcement
178
+ - **🌱 Seeding Control** – Per-tracker settings, ratio/time limits, tracker injection
179
+ - **💾 Disk Space Management** – Auto-pause when low on space, configurable thresholds
180
+ - **🔄 Auto-Updates** – GitHub release-based updates with scheduled cron support
181
+ - **💻 Modern WebUI** – Live process monitoring, log viewer, Arr insights, config editor
182
+
183
+ ## 🛠️ Essential Configuration
184
+
185
+ 1. **Configure qBittorrent** in `~/config/config.toml`:
186
+ ```toml
187
+ [qBit]
188
+ Host = "localhost"
189
+ Port = 8080
190
+ UserName = "admin"
191
+ Password = "adminpass"
192
+ Version5 = true # qBittorrent 5.x
193
+ ```
194
+
195
+ 2. **Add Arr instances**:
196
+ ```toml
197
+ [Radarr-Movies]
198
+ URI = "http://localhost:7878"
199
+ APIKey = "your-radarr-api-key"
200
+ Category = "radarr-movies"
201
+ ```
202
+
203
+ 3. **Set completed folder**:
204
+ ```toml
205
+ [Settings]
206
+ CompletedDownloadFolder = "/path/to/completed"
207
+ ```
208
+
209
+ See [Configuration Guide](https://feramance.github.io/qBitrr/configuration/) and [config.example.toml](config.example.toml) for all available options.
210
+
211
+ ## 📖 Resources
212
+
213
+ - **Documentation:** https://feramance.github.io/qBitrr/
214
+ - **PyPI Package:** https://pypi.org/project/qBitrr2/
215
+ - **Docker Hub:** https://hub.docker.com/r/feramance/qbitrr
216
+ - **Example Config:** [config.example.toml](config.example.toml)
217
+ - **API Documentation:** [API_DOCUMENTATION.md](API_DOCUMENTATION.md)
218
+ - **Systemd Setup:** [SYSTEMD_SERVICE.md](SYSTEMD_SERVICE.md)
219
+
220
+ ## 🐛 Issues & Support
221
+
222
+ - **Report Bugs:** [Bug Report Template](.github/ISSUE_TEMPLATE/bug_report.yml)
223
+ - **Request Features:** [Feature Request Template](.github/ISSUE_TEMPLATE/feature_request.yml)
224
+ - **Discussions:** [GitHub Discussions](https://github.com/Feramance/qBitrr/discussions)
225
+ - **Troubleshooting:** [Common Issues](https://feramance.github.io/qBitrr/troubleshooting/)
226
+
227
+ ## 🤝 Contributing
228
+
229
+ Contributions welcome! See [CONTRIBUTION.md](CONTRIBUTION.md) for coding guidelines and development setup.
230
+
231
+ **Development setup:**
232
+ ```bash
233
+ # Python backend
234
+ make newenv && make syncenv
235
+ make reformat # Format and lint
236
+
237
+ # WebUI
238
+ cd webui && npm ci
239
+ npm run dev # Dev server at localhost:5173
240
+ ```
241
+
242
+ ## ❤️ Support
243
+
244
+ If qBitrr saves you time and headaches:
245
+ - ⭐ **Star the repo** – helps others discover qBitrr
246
+ - 💰 **Sponsor:** [Patreon](https://patreon.com/qBitrr) | [PayPal](https://www.paypal.me/feramance)
247
+
248
+ ## 📄 License
249
+
250
+ Released under the [MIT License](LICENSE). Use it, modify it, share it—commercially or personally.
251
+
252
+ ---
253
+
254
+ <div align="center">
255
+
256
+ **Made with ❤️ by the qBitrr community**
257
+
258
+ [Documentation](https://feramance.github.io/qBitrr/) • [PyPI](https://pypi.org/project/qBitrr2/) • [Docker](https://hub.docker.com/r/feramance/qbitrr) • [GitHub](https://github.com/Feramance/qBitrr)
259
+
260
+ </div>
@@ -0,0 +1,156 @@
1
+ # <img src="assets/logov2-clean.png" alt="qBitrr Logo" width="40" style="vertical-align: middle;"/> qBitrr
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/qBitrr2?label=PyPI)](https://pypi.org/project/qBitrr2/)
4
+ [![Downloads](https://img.shields.io/pypi/dm/qBitrr2)](https://pypi.org/project/qBitrr2/)
5
+ [![Docker Pulls](https://img.shields.io/docker/pulls/feramance/qbitrr.svg)](https://hub.docker.com/r/feramance/qbitrr)
6
+ [![CodeQL](https://github.com/Feramance/qBitrr/actions/workflows/codeql.yml/badge.svg?branch=master)](https://github.com/Feramance/qBitrr/actions/workflows/codeql.yml)
7
+ [![Nightly Build](https://github.com/Feramance/qBitrr/actions/workflows/nightly.yml/badge.svg?branch=master)](https://github.com/Feramance/qBitrr/actions/workflows/nightly.yml)
8
+ [![pre-commit.ci](https://results.pre-commit.ci/badge/github/Feramance/qBitrr/master.svg)](https://results.pre-commit.ci/latest/github/Feramance/qBitrr/master)
9
+ [![License: MIT](https://img.shields.io/pypi/l/qbitrr)](LICENSE)
10
+
11
+ > 🧩 The intelligent glue between qBittorrent and the *Arr ecosystem (Radarr, Sonarr, Lidarr). Monitors torrent health, triggers instant imports, automates quality upgrades, manages disk space, integrates with request systems (Overseerr/Ombi), and provides a modern React dashboard for complete visibility and control.
12
+
13
+ ## 📚 Documentation
14
+
15
+ **Full documentation is available at: https://feramance.github.io/qBitrr/**
16
+
17
+ - [Getting Started](https://feramance.github.io/qBitrr/getting-started/) – Installation guides for pip, Docker, and native setups
18
+ - [Configuration](https://feramance.github.io/qBitrr/configuration/) – qBittorrent, Arr instances, quality profiles, and more
19
+ - [Features](https://feramance.github.io/qBitrr/features/) – Health monitoring, automated search, quality management, disk space, auto-updates
20
+ - [WebUI](https://feramance.github.io/qBitrr/webui/) – Built-in React dashboard with live monitoring and config editor
21
+ - [Troubleshooting](https://feramance.github.io/qBitrr/troubleshooting/) – Common issues and debug logging
22
+ - [API Reference](https://feramance.github.io/qBitrr/reference/api/) – REST API documentation
23
+
24
+ ## ⚡ Quick Start
25
+
26
+ ### 🐍 Install with pip
27
+ ```bash
28
+ python -m venv .venv
29
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
30
+ pip install qBitrr2
31
+
32
+ # First run creates ~/config/config.toml
33
+ qbitrr
34
+ ```
35
+
36
+ ### 🐳 Run with Docker
37
+ ```bash
38
+ docker run -d \
39
+ --name qbitrr \
40
+ --tty \
41
+ -e TZ=Europe/London \
42
+ -p 6969:6969 \
43
+ -v /path/to/appdata/qbitrr:/config \
44
+ -v /path/to/completed/downloads:/completed_downloads:rw \
45
+ --restart unless-stopped \
46
+ feramance/qbitrr:latest
47
+ ```
48
+
49
+ **Docker Compose:**
50
+ ```yaml
51
+ services:
52
+ qbitrr:
53
+ image: feramance/qbitrr:latest
54
+ container_name: qbitrr
55
+ restart: unless-stopped
56
+ tty: true
57
+ environment:
58
+ TZ: Europe/London
59
+ ports:
60
+ - "6969:6969"
61
+ volumes:
62
+ - /path/to/appdata/qbitrr:/config
63
+ - /path/to/completed/downloads:/completed_downloads:rw
64
+ ```
65
+
66
+ Access the WebUI at `http://<host>:6969/ui` after startup.
67
+
68
+ ## ✨ Key Features
69
+
70
+ - **🚑 Torrent Health Monitoring** – Detect stalled/failed downloads, auto-blacklist, trigger re-searches
71
+ - **🔍 Automated Search** – Missing media, quality upgrades, custom format scoring
72
+ - **🎯 Request Integration** – Pull requests from Overseerr/Ombi, prioritize user-requested media
73
+ - **📊 Quality Management** – RSS sync, queue refresh, profile switching, custom format enforcement
74
+ - **🌱 Seeding Control** – Per-tracker settings, ratio/time limits, tracker injection
75
+ - **💾 Disk Space Management** – Auto-pause when low on space, configurable thresholds
76
+ - **🔄 Auto-Updates** – GitHub release-based updates with scheduled cron support
77
+ - **💻 Modern WebUI** – Live process monitoring, log viewer, Arr insights, config editor
78
+
79
+ ## 🛠️ Essential Configuration
80
+
81
+ 1. **Configure qBittorrent** in `~/config/config.toml`:
82
+ ```toml
83
+ [qBit]
84
+ Host = "localhost"
85
+ Port = 8080
86
+ UserName = "admin"
87
+ Password = "adminpass"
88
+ Version5 = true # qBittorrent 5.x
89
+ ```
90
+
91
+ 2. **Add Arr instances**:
92
+ ```toml
93
+ [Radarr-Movies]
94
+ URI = "http://localhost:7878"
95
+ APIKey = "your-radarr-api-key"
96
+ Category = "radarr-movies"
97
+ ```
98
+
99
+ 3. **Set completed folder**:
100
+ ```toml
101
+ [Settings]
102
+ CompletedDownloadFolder = "/path/to/completed"
103
+ ```
104
+
105
+ See [Configuration Guide](https://feramance.github.io/qBitrr/configuration/) and [config.example.toml](config.example.toml) for all available options.
106
+
107
+ ## 📖 Resources
108
+
109
+ - **Documentation:** https://feramance.github.io/qBitrr/
110
+ - **PyPI Package:** https://pypi.org/project/qBitrr2/
111
+ - **Docker Hub:** https://hub.docker.com/r/feramance/qbitrr
112
+ - **Example Config:** [config.example.toml](config.example.toml)
113
+ - **API Documentation:** [API_DOCUMENTATION.md](API_DOCUMENTATION.md)
114
+ - **Systemd Setup:** [SYSTEMD_SERVICE.md](SYSTEMD_SERVICE.md)
115
+
116
+ ## 🐛 Issues & Support
117
+
118
+ - **Report Bugs:** [Bug Report Template](.github/ISSUE_TEMPLATE/bug_report.yml)
119
+ - **Request Features:** [Feature Request Template](.github/ISSUE_TEMPLATE/feature_request.yml)
120
+ - **Discussions:** [GitHub Discussions](https://github.com/Feramance/qBitrr/discussions)
121
+ - **Troubleshooting:** [Common Issues](https://feramance.github.io/qBitrr/troubleshooting/)
122
+
123
+ ## 🤝 Contributing
124
+
125
+ Contributions welcome! See [CONTRIBUTION.md](CONTRIBUTION.md) for coding guidelines and development setup.
126
+
127
+ **Development setup:**
128
+ ```bash
129
+ # Python backend
130
+ make newenv && make syncenv
131
+ make reformat # Format and lint
132
+
133
+ # WebUI
134
+ cd webui && npm ci
135
+ npm run dev # Dev server at localhost:5173
136
+ ```
137
+
138
+ ## ❤️ Support
139
+
140
+ If qBitrr saves you time and headaches:
141
+ - ⭐ **Star the repo** – helps others discover qBitrr
142
+ - 💰 **Sponsor:** [Patreon](https://patreon.com/qBitrr) | [PayPal](https://www.paypal.me/feramance)
143
+
144
+ ## 📄 License
145
+
146
+ Released under the [MIT License](LICENSE). Use it, modify it, share it—commercially or personally.
147
+
148
+ ---
149
+
150
+ <div align="center">
151
+
152
+ **Made with ❤️ by the qBitrr community**
153
+
154
+ [Documentation](https://feramance.github.io/qBitrr/) • [PyPI](https://pypi.org/project/qBitrr2/) • [Docker](https://hub.docker.com/r/feramance/qbitrr) • [GitHub](https://github.com/Feramance/qBitrr)
155
+
156
+ </div>
@@ -28,7 +28,7 @@ target-version = ['py311']
28
28
 
29
29
  [tool.poetry]
30
30
  name = "pypi-public"
31
- version = "5.6.0"
31
+ version = "5.6.2"
32
32
  description = "Intelligent automation for qBittorrent and *Arr apps (Radarr/Sonarr/Lidarr) - health monitoring, instant imports, quality upgrades, request integration"
33
33
  authors = ["Drapersniper", "Feramance"]
34
34
  readme = "README.md"
@@ -20,7 +20,7 @@ import qbittorrentapi
20
20
  import qbittorrentapi.exceptions
21
21
  import requests
22
22
  from packaging import version as version_parser
23
- from peewee import Model, SqliteDatabase
23
+ from peewee import DatabaseError, Model, OperationalError, SqliteDatabase
24
24
  from pyarr import LidarrAPI, RadarrAPI, SonarrAPI
25
25
  from pyarr.exceptions import PyarrResourceNotFound, PyarrServerError
26
26
  from pyarr.types import JsonObject
@@ -4475,6 +4475,11 @@ class Arr:
4475
4475
  if self.manager.qbit_manager.should_delay_torrent_scan:
4476
4476
  raise DelayLoopException(length=NO_INTERNET_SLEEP_TIMER, type="delay")
4477
4477
 
4478
+ # Initialize database error tracking for exponential backoff
4479
+ if not hasattr(self, "_db_error_count"):
4480
+ self._db_error_count = 0
4481
+ self._db_last_error_time = 0
4482
+
4478
4483
  # Periodic database health check (every 10th iteration)
4479
4484
  if not hasattr(self, "_health_check_counter"):
4480
4485
  self._health_check_counter = 0
@@ -4516,6 +4521,76 @@ class Arr:
4516
4521
  self.logger.error("The qBittorrent API returned an unexpected error")
4517
4522
  self.logger.debug("Unexpected APIError from qBitTorrent") # , exc_info=e)
4518
4523
  raise DelayLoopException(length=300, type="qbit")
4524
+ except (OperationalError, DatabaseError) as e:
4525
+ # Database errors after retry exhaustion - implement automatic recovery with backoff
4526
+ error_msg = str(e).lower()
4527
+ current_time = time.time()
4528
+
4529
+ # Track consecutive database errors for exponential backoff
4530
+ if (
4531
+ current_time - self._db_last_error_time > 300
4532
+ ): # Reset if >5min since last error
4533
+ self._db_error_count = 0
4534
+ self._db_error_count += 1
4535
+ self._db_last_error_time = current_time
4536
+
4537
+ # Calculate exponential backoff: 2min, 5min, 10min, 20min, 30min (max)
4538
+ delay_seconds = min(120 * (2 ** (self._db_error_count - 1)), 1800)
4539
+
4540
+ # Log detailed error information based on error type
4541
+ if "disk i/o error" in error_msg:
4542
+ self.logger.critical(
4543
+ "Persistent database I/O error detected (consecutive error #%d). "
4544
+ "This indicates disk issues, filesystem corruption, or resource exhaustion. "
4545
+ "Attempting automatic recovery and retrying in %d seconds...",
4546
+ self._db_error_count,
4547
+ delay_seconds,
4548
+ )
4549
+ elif "database is locked" in error_msg:
4550
+ self.logger.error(
4551
+ "Database locked error (consecutive error #%d). "
4552
+ "Retrying in %d seconds...",
4553
+ self._db_error_count,
4554
+ delay_seconds,
4555
+ )
4556
+ elif "disk image is malformed" in error_msg:
4557
+ self.logger.critical(
4558
+ "Database corruption detected (consecutive error #%d). "
4559
+ "Attempting automatic recovery and retrying in %d seconds...",
4560
+ self._db_error_count,
4561
+ delay_seconds,
4562
+ )
4563
+ else:
4564
+ self.logger.error(
4565
+ "Database error (consecutive error #%d): %s. " "Retrying in %d seconds...",
4566
+ self._db_error_count,
4567
+ error_msg,
4568
+ delay_seconds,
4569
+ )
4570
+
4571
+ # Attempt automatic recovery for critical errors
4572
+ if "disk i/o error" in error_msg or "disk image is malformed" in error_msg:
4573
+ try:
4574
+ self.logger.warning(
4575
+ "Attempting enhanced database recovery (WAL checkpoint, repair, and verification)..."
4576
+ )
4577
+ self._enhanced_database_recovery()
4578
+ self.logger.info(
4579
+ "Database recovery completed successfully - will retry operation after delay"
4580
+ )
4581
+ # Reduce error count on successful recovery (but don't reset completely)
4582
+ self._db_error_count = max(0, self._db_error_count - 1)
4583
+ except Exception as recovery_error:
4584
+ self.logger.critical(
4585
+ "Automatic database recovery failed: %s. "
4586
+ "MANUAL INTERVENTION REQUIRED: Check disk health (smartctl), "
4587
+ "filesystem integrity (fsck), available space (df -h), "
4588
+ "Docker volume mounts, permissions, and system logs (dmesg).",
4589
+ recovery_error,
4590
+ )
4591
+
4592
+ # Delay processing to avoid hammering failing database
4593
+ raise DelayLoopException(length=delay_seconds, type="database")
4519
4594
  except DelayLoopException:
4520
4595
  raise
4521
4596
  except KeyboardInterrupt:
@@ -4566,6 +4641,83 @@ class Arr:
4566
4641
  "Manual intervention may be required. Continuing with caution..."
4567
4642
  )
4568
4643
 
4644
+ def _enhanced_database_recovery(self):
4645
+ """
4646
+ Enhanced automatic database recovery with additional filesystem checks.
4647
+
4648
+ This method is called when disk I/O errors persist after retry logic has been exhausted.
4649
+ It implements a comprehensive recovery strategy:
4650
+ 1. Try WAL checkpoint (least invasive)
4651
+ 2. Try VACUUM to reclaim space and fix minor corruption
4652
+ 3. Try full database repair (dump/restore) if needed
4653
+ 4. Verify database integrity after recovery
4654
+ """
4655
+ from qBitrr.db_recovery import (
4656
+ DatabaseRecoveryError,
4657
+ checkpoint_wal,
4658
+ repair_database,
4659
+ vacuum_database,
4660
+ )
4661
+ from qBitrr.home_path import APPDATA_FOLDER
4662
+
4663
+ db_path = APPDATA_FOLDER / "qbitrr.db"
4664
+
4665
+ self.logger.info("Starting enhanced database recovery procedure...")
4666
+
4667
+ # Step 1: Try WAL checkpoint
4668
+ self.logger.info("Step 1/3: Attempting WAL checkpoint...")
4669
+ if checkpoint_wal(db_path, self.logger):
4670
+ self.logger.info("WAL checkpoint successful")
4671
+ # Try a quick health check
4672
+ from qBitrr.db_lock import check_database_health
4673
+
4674
+ healthy, msg = check_database_health(db_path, self.logger)
4675
+ if healthy:
4676
+ self.logger.info("Database health verified - recovery complete")
4677
+ return
4678
+ else:
4679
+ self.logger.warning(
4680
+ "WAL checkpoint completed but database still unhealthy: %s", msg
4681
+ )
4682
+
4683
+ # Step 2: Try VACUUM (only if WAL didn't fully fix it)
4684
+ self.logger.info("Step 2/3: Attempting VACUUM to reclaim space and fix minor issues...")
4685
+ if vacuum_database(db_path, self.logger):
4686
+ self.logger.info("VACUUM completed successfully")
4687
+ from qBitrr.db_lock import check_database_health
4688
+
4689
+ healthy, msg = check_database_health(db_path, self.logger)
4690
+ if healthy:
4691
+ self.logger.info("Database health verified after VACUUM - recovery complete")
4692
+ return
4693
+ else:
4694
+ self.logger.warning("VACUUM completed but database still unhealthy: %s", msg)
4695
+
4696
+ # Step 3: Try full repair (most invasive)
4697
+ self.logger.warning("Step 3/3: Attempting full database repair (dump/restore)...")
4698
+ try:
4699
+ if repair_database(db_path, backup=True, logger_override=self.logger):
4700
+ self.logger.info("Database repair successful")
4701
+ # Final health check
4702
+ from qBitrr.db_lock import check_database_health
4703
+
4704
+ healthy, msg = check_database_health(db_path, self.logger)
4705
+ if healthy:
4706
+ self.logger.info("Database health verified after repair - recovery complete")
4707
+ return
4708
+ else:
4709
+ self.logger.error("Repair completed but database still unhealthy: %s", msg)
4710
+ raise DatabaseRecoveryError(f"Database unhealthy after repair: {msg}")
4711
+ except DatabaseRecoveryError as e:
4712
+ self.logger.error("Database repair failed: %s", e)
4713
+ raise
4714
+ except Exception as e:
4715
+ self.logger.error("Unexpected error during database repair: %s", e)
4716
+ raise
4717
+
4718
+ # If we reach here, all recovery methods failed
4719
+ raise DatabaseRecoveryError("All automatic recovery methods failed")
4720
+
4569
4721
  def _process_single_torrent_failed_cat(self, torrent: qbittorrentapi.TorrentDictionary):
4570
4722
  self.logger.notice(
4571
4723
  "Deleting manually failed torrent: "
@@ -1,5 +1,5 @@
1
- version = "5.6.0"
2
- git_hash = "5776d71b"
1
+ version = "5.6.2"
2
+ git_hash = "e39e0d1e"
3
3
  license_text = (
4
4
  "Licence can be found on:\n\nhttps://github.com/Feramance/qBitrr/blob/master/LICENSE"
5
5
  )