mirror of
https://github.com/StuffAnThings/qbit_manage.git
synced 2025-09-13 00:24:45 +08:00
4.2.1 (#768)
* 4.2.1-develop1 * Adds #747 * Fixes #764 * Replaced pre-commit hooks flake8, black, pyupgrade, etc. with ruff for linting and formatting. * Bump ruff from 0.9.10 to 0.10.0 (#767) Bumps [ruff](https://github.com/astral-sh/ruff) from 0.9.10 to 0.10.0. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.9.10...0.10.0) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * 4.2.1 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
parent
fc24d1d934
commit
8478409027
23 changed files with 134 additions and 96 deletions
7
.flake8
7
.flake8
|
@ -1,7 +0,0 @@
|
|||
[flake8]
|
||||
extend-ignore =
|
||||
# E722 Do not use bare except, specify exception instead
|
||||
E722,
|
||||
# E402 module level import not at top of file
|
||||
E402,
|
||||
max-line-length = 130
|
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
|
@ -28,3 +28,12 @@ jobs:
|
|||
- name: Run pre-commit version check
|
||||
run: |
|
||||
pre-commit run increase-version --all-files
|
||||
|
||||
ruff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
command: 'ruff check'
|
||||
|
|
|
@ -8,18 +8,11 @@ repos:
|
|||
- id: check-merge-conflict
|
||||
- id: check-json
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: requirements-txt-fixer
|
||||
- id: check-added-large-files
|
||||
- id: fix-byte-order-marker
|
||||
- id: fix-encoding-pragma
|
||||
args: [--remove]
|
||||
- id: pretty-format-json
|
||||
args: [--autofix, --indent, '4', --no-sort-keys]
|
||||
- repo: https://github.com/hhatto/autopep8
|
||||
rev: v2.3.2
|
||||
hooks:
|
||||
- id: autopep8
|
||||
- repo: https://github.com/adrienverge/yamllint.git
|
||||
rev: v1.35.1 # or higher tag
|
||||
hooks:
|
||||
|
@ -31,28 +24,15 @@ repos:
|
|||
hooks:
|
||||
- id: yamlfix
|
||||
exclude: ^.github/
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 6.0.0
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.9.10
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort (python)
|
||||
args: [--force-single-line-imports, --profile, black]
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.19.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py3-plus]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 25.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3
|
||||
args: [--line-length, '130']
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 7.1.2
|
||||
hooks:
|
||||
- id: flake8
|
||||
args: [--config=.flake8]
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: increase-version
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
# New Updates
|
||||
- Removes deprecated cross-seed command/function (#758)
|
||||
- Adds ability to tag stalled downloads (#757)
|
||||
- Adds ability to customize a list of status to ignore during rem_unregistered command (#756)
|
||||
- Adds new command to filter for completed torrents only for rem_unregistered (#747)
|
||||
- Replace flake8/black with ruff for lint and formatting
|
||||
|
||||
# Bug Fixes
|
||||
- Fixes bug in Windows during category updates (#755)
|
||||
- Fixes bug in tagging `stalledDL` torrents when set to False (#764)
|
||||
|
||||
**Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.1.18...v4.2.0
|
||||
**Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.2.0...v4.2.1
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
[](https://ghcr.io/StuffAnThings/qbit_manage)
|
||||
[](https://hub.docker.com/r/bobokun/qbit_manage)
|
||||
[](https://github.com/sponsors/bobokun)
|
||||
|
||||
[](https://github.com/astral-sh/ruff)
|
||||
This is a program used to manage your qBittorrent instance such as:
|
||||
|
||||
* Tag torrents based on tracker URLs
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
4.2.0
|
||||
4.2.1
|
||||
|
|
|
@ -39,6 +39,7 @@ settings:
|
|||
cat_filter_completed: True # Filters for completed torrents only when running cat_update command
|
||||
share_limits_filter_completed: True # Filters for completed torrents only when running share_limits command
|
||||
tag_nohardlinks_filter_completed: True # Filters for completed torrents only when running tag_nohardlinks command
|
||||
rem_unregistered_filter_completed: False # Filters for completed torrents only when running rem_unregistered command
|
||||
cat_update_all: True # Checks and updates all torrent categories if set to True when running cat_update command, otherwise only update torrents that are uncategorized
|
||||
disable_qbt_default_share_limits: True # Allows QBM to handle share limits by disabling qBittorrents default Share limits. Only active when the share_limits command is set to True
|
||||
tag_stalled_torrents: True # Tags any downloading torrents that are stalled with the `stalledDL` tag when running the tag_update command
|
||||
|
|
|
@ -223,6 +223,9 @@ class Config:
|
|||
"tag_nohardlinks_filter_completed": self.util.check_for_attribute(
|
||||
self.data, "tag_nohardlinks_filter_completed", parent="settings", var_type="bool", default=True
|
||||
),
|
||||
"rem_unregistered_filter_completed": self.util.check_for_attribute(
|
||||
self.data, "rem_unregistered_filter_completed", parent="settings", var_type="bool", default=False
|
||||
),
|
||||
"cat_update_all": self.util.check_for_attribute(
|
||||
self.data, "cat_update_all", parent="settings", var_type="bool", default=True
|
||||
),
|
||||
|
@ -847,7 +850,8 @@ class Config:
|
|||
try:
|
||||
fileStats = os.stat(file)
|
||||
filename = os.path.basename(file)
|
||||
last_modified = fileStats[stat.ST_MTIME] # in seconds (last modified time)
|
||||
# in seconds (last modified time)
|
||||
last_modified = fileStats[stat.ST_MTIME]
|
||||
except FileNotFoundError:
|
||||
ex = logger.print_line(
|
||||
f"{location} Warning - FileNotFound: No such file or directory: {file} ", "WARNING"
|
||||
|
|
|
@ -84,7 +84,7 @@ class Category:
|
|||
else:
|
||||
title = "Updating Categories"
|
||||
body += logger.print_line(logger.insert_space(f"New Category: {new_cat}", 3), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Tracker: {tracker['url']}", 8), self.config.loglevel)
|
||||
attr = {
|
||||
"function": "cat_update",
|
||||
"title": title,
|
||||
|
|
|
@ -14,7 +14,8 @@ class ReCheck:
|
|||
self.stats_rechecked = 0
|
||||
|
||||
self.torrents_updated_recheck = [] # List of torrents updated
|
||||
self.notify_attr_recheck = [] # List of single torrent attributes to send to notifiarr
|
||||
# List of single torrent attributes to send to notifiarr
|
||||
self.notify_attr_recheck = []
|
||||
self.torrents_updated_resume = [] # List of torrents updated
|
||||
self.notify_attr_resume = [] # List of single torrent attributes to send to notifiarr
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ class RemoveUnregistered:
|
|||
self.cfg_rem_unregistered = self.config.commands["rem_unregistered"]
|
||||
self.cfg_tag_error = self.config.commands["tag_tracker_error"]
|
||||
self.rem_unregistered_ignore_list = self.config.settings["rem_unregistered_ignore_list"]
|
||||
self.filter_completed = self.config.settings["rem_unregistered_filter_completed"]
|
||||
|
||||
tag_error_msg = "Tagging Torrents with Tracker Errors" if self.cfg_tag_error else ""
|
||||
rem_unregistered_msg = "Removing Unregistered Torrents" if self.cfg_rem_unregistered else ""
|
||||
|
@ -56,7 +57,7 @@ class RemoveUnregistered:
|
|||
)
|
||||
body += logger.print_line(logger.insert_space(f"Torrent Name: {t_name}", 3), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Removed Tag: {self.tag_error}", 4), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Tracker: {tracker['url']}", 8), self.config.loglevel)
|
||||
if not self.config.dry_run:
|
||||
torrent.remove_tags(tags=self.tag_error)
|
||||
attr = {
|
||||
|
@ -108,6 +109,8 @@ class RemoveUnregistered:
|
|||
self.t_status = self.qbt.torrentinfo[self.t_name]["status"]
|
||||
check_tags = util.get_list(torrent.tags)
|
||||
try:
|
||||
if self.filter_completed and not torrent.state_enum.is_complete:
|
||||
continue
|
||||
tracker_working = False
|
||||
for trk in torrent.trackers:
|
||||
if (
|
||||
|
@ -190,7 +193,7 @@ class RemoveUnregistered:
|
|||
tor_error = ""
|
||||
tor_error += logger.insert_space(f"Torrent Name: {self.t_name}", 3) + "\n"
|
||||
tor_error += logger.insert_space(f"Status: {msg}", 9) + "\n"
|
||||
tor_error += logger.insert_space(f'Tracker: {tracker["url"]}', 8) + "\n"
|
||||
tor_error += logger.insert_space(f"Tracker: {tracker['url']}", 8) + "\n"
|
||||
tor_error += logger.insert_space(f"Added Tag: {self.tag_error}", 6) + "\n"
|
||||
self.tor_error_summary += tor_error
|
||||
self.stats_tagged += 1
|
||||
|
@ -215,7 +218,7 @@ class RemoveUnregistered:
|
|||
body = []
|
||||
body += logger.print_line(logger.insert_space(f"Torrent Name: {self.t_name}", 3), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Status: {msg}", 9), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Tracker: {tracker['url']}", 8), self.config.loglevel)
|
||||
attr = {
|
||||
"function": "rem_unregistered",
|
||||
"title": "Removing Unregistered Torrents",
|
||||
|
|
|
@ -21,17 +21,24 @@ class ShareLimits:
|
|||
# meets the criteria for ratio limit/seed limit for deletion including contents \
|
||||
self.status_filter = "completed" if self.config.settings["share_limits_filter_completed"] else "all"
|
||||
|
||||
self.tdel_dict = {} # dictionary to track the torrent names and content path that meet the deletion criteria
|
||||
# dictionary to track the torrent names and content path that meet the deletion criteria
|
||||
self.tdel_dict = {}
|
||||
self.root_dir = qbit_manager.config.root_dir # root directory of torrents
|
||||
self.remote_dir = qbit_manager.config.remote_dir # remote directory of torrents
|
||||
self.share_limits_config = qbit_manager.config.share_limits # configuration of share limits
|
||||
# configuration of share limits
|
||||
self.share_limits_config = qbit_manager.config.share_limits
|
||||
self.torrents_updated = [] # list of torrents that have been updated
|
||||
self.torrent_hash_checked = [] # list of torrent hashes that have been checked for share limits
|
||||
# list of torrent hashes that have been checked for share limits
|
||||
self.torrent_hash_checked = []
|
||||
self.share_limits_tag = qbit_manager.config.share_limits_tag # tag for share limits
|
||||
self.share_limits_custom_tags = qbit_manager.config.share_limits_custom_tags # All possible custom share limits tags
|
||||
self.min_seeding_time_tag = qbit_manager.config.share_limits_min_seeding_time_tag # tag for min seeding time
|
||||
self.min_num_seeds_tag = qbit_manager.config.share_limits_min_num_seeds_tag # tag for min num seeds
|
||||
self.last_active_tag = qbit_manager.config.share_limits_last_active_tag # tag for last active
|
||||
# All possible custom share limits tags
|
||||
self.share_limits_custom_tags = qbit_manager.config.share_limits_custom_tags
|
||||
# tag for min seeding time
|
||||
self.min_seeding_time_tag = qbit_manager.config.share_limits_min_seeding_time_tag
|
||||
# tag for min num seeds
|
||||
self.min_num_seeds_tag = qbit_manager.config.share_limits_min_num_seeds_tag
|
||||
# tag for last active
|
||||
self.last_active_tag = qbit_manager.config.share_limits_last_active_tag
|
||||
self.group_tag = None # tag for the share limit group
|
||||
|
||||
self.update_share_limits()
|
||||
|
@ -87,7 +94,7 @@ class ShareLimits:
|
|||
tracker = self.qbt.get_tags(self.qbt.get_tracker_urls(torrent.trackers))
|
||||
body = []
|
||||
body += logger.print_line(logger.insert_space(f"Torrent Name: {t_name}", 3), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Tracker: {tracker['url']}", 8), self.config.loglevel)
|
||||
body += logger.print_line(torrent_dict["body"], self.config.loglevel)
|
||||
body += logger.print_line(
|
||||
logger.insert_space("Cleanup: True [Meets Share Limits]", 8),
|
||||
|
@ -215,7 +222,8 @@ class ShareLimits:
|
|||
else:
|
||||
share_limits_not_yet_tagged = False
|
||||
|
||||
check_multiple_share_limits_tag = False # Default assume no multiple share limits tag
|
||||
# Default assume no multiple share limits tag
|
||||
check_multiple_share_limits_tag = False
|
||||
|
||||
# Check if any of the previous share limits custom tags are there
|
||||
for custom_tag in self.share_limits_custom_tags:
|
||||
|
@ -296,7 +304,7 @@ class ShareLimits:
|
|||
or check_multiple_share_limits_tag
|
||||
):
|
||||
logger.print_line(logger.insert_space(f"Torrent Name: {t_name}", 3), self.config.loglevel)
|
||||
logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
logger.print_line(logger.insert_space(f"Tracker: {tracker['url']}", 8), self.config.loglevel)
|
||||
if self.group_tag:
|
||||
logger.print_line(logger.insert_space(f"Added Tag: {self.group_tag}", 8), self.config.loglevel)
|
||||
self.tag_and_update_share_limits_for_torrent(torrent, group_config)
|
||||
|
|
|
@ -9,7 +9,8 @@ class TagNoHardLinks:
|
|||
self.config = qbit_manager.config
|
||||
self.client = qbit_manager.client
|
||||
self.stats_tagged = 0 # counter for the number of torrents that has no hardlinks
|
||||
self.stats_untagged = 0 # counter for number of torrents that previously had no hardlinks but now have hardlinks
|
||||
# counter for number of torrents that previously had no hardlinks but now have hardlinks
|
||||
self.stats_untagged = 0
|
||||
|
||||
self.root_dir = qbit_manager.config.root_dir
|
||||
self.remote_dir = qbit_manager.config.remote_dir
|
||||
|
@ -20,7 +21,8 @@ class TagNoHardLinks:
|
|||
self.notify_attr_tagged = [] # List of single torrent attributes to send to notifiarr
|
||||
|
||||
self.torrents_updated_untagged = [] # List of torrents updated
|
||||
self.notify_attr_untagged = [] # List of single torrent attributes to send to notifiarr
|
||||
# List of single torrent attributes to send to notifiarr
|
||||
self.notify_attr_untagged = []
|
||||
|
||||
self.status_filter = "completed" if self.config.settings["tag_nohardlinks_filter_completed"] else "all"
|
||||
|
||||
|
@ -35,7 +37,7 @@ class TagNoHardLinks:
|
|||
body.append(logger.insert_space(f"Torrent Name: {torrent.name}", 3))
|
||||
body.append(logger.insert_space(f"Added Tag: {self.nohardlinks_tag}", 6))
|
||||
title = "Tagging Torrents with No Hardlinks"
|
||||
body.append(logger.insert_space(f'Tracker: {tracker["url"]}', 8))
|
||||
body.append(logger.insert_space(f"Tracker: {tracker['url']}", 8))
|
||||
if not self.config.dry_run:
|
||||
torrent.add_tags(self.nohardlinks_tag)
|
||||
self.stats_tagged += 1
|
||||
|
@ -67,7 +69,7 @@ class TagNoHardLinks:
|
|||
self.config.loglevel,
|
||||
)
|
||||
body += logger.print_line(logger.insert_space(f"Removed Tag: {self.nohardlinks_tag}", 6), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Tracker: {tracker['url']}", 8), self.config.loglevel)
|
||||
if not self.config.dry_run:
|
||||
torrent.remove_tags(tags=self.nohardlinks_tag)
|
||||
attr = {
|
||||
|
|
|
@ -9,10 +9,12 @@ class Tags:
|
|||
self.config = qbit_manager.config
|
||||
self.client = qbit_manager.client
|
||||
self.stats = 0
|
||||
self.share_limits_tag = qbit_manager.config.share_limits_tag # suffix tag for share limits
|
||||
# suffix tag for share limits
|
||||
self.share_limits_tag = qbit_manager.config.share_limits_tag
|
||||
self.torrents_updated = [] # List of torrents updated
|
||||
self.notify_attr = [] # List of single torrent attributes to send to notifiarr
|
||||
self.stalled_tag = "stalledDL"
|
||||
self.tag_stalled_torrents = self.config.settings["tag_stalled_torrents"]
|
||||
|
||||
self.tags()
|
||||
self.config.webhooks_factory.notify(self.torrents_updated, self.notify_attr, group_by="tag")
|
||||
|
@ -24,12 +26,16 @@ class Tags:
|
|||
tracker = self.qbt.get_tags(self.qbt.get_tracker_urls(torrent.trackers))
|
||||
|
||||
# Remove stalled_tag if torrent is no longer stalled
|
||||
if util.is_tag_in_torrent(self.stalled_tag, torrent.tags) and torrent.state != "stalledDL":
|
||||
if (
|
||||
self.tag_stalled_torrents
|
||||
and util.is_tag_in_torrent(self.stalled_tag, torrent.tags)
|
||||
and torrent.state != "stalledDL"
|
||||
):
|
||||
t_name = torrent.name
|
||||
body = []
|
||||
body += logger.print_line(logger.insert_space(f"Torrent Name: {t_name}", 3), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Removing Tag: {self.stalled_tag}", 3), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Tracker: {tracker['url']}", 8), self.config.loglevel)
|
||||
if not self.config.dry_run:
|
||||
torrent.remove_tags(self.stalled_tag)
|
||||
if (
|
||||
|
@ -38,7 +44,7 @@ class Tags:
|
|||
or (torrent.state == "stalledDL" and not util.is_tag_in_torrent(self.stalled_tag, torrent.tags))
|
||||
):
|
||||
stalled = False
|
||||
if torrent.state == "stalledDL":
|
||||
if self.tag_stalled_torrents and torrent.state == "stalledDL":
|
||||
stalled = True
|
||||
tracker["tag"].append(self.stalled_tag)
|
||||
if tracker["tag"] or stalled:
|
||||
|
@ -47,10 +53,10 @@ class Tags:
|
|||
body = []
|
||||
body += logger.print_line(logger.insert_space(f"Torrent Name: {t_name}", 3), self.config.loglevel)
|
||||
body += logger.print_line(
|
||||
logger.insert_space(f'New Tag{"s" if len(tracker["tag"]) > 1 else ""}: {", ".join(tracker["tag"])}', 8),
|
||||
logger.insert_space(f"New Tag{'s' if len(tracker['tag']) > 1 else ''}: {', '.join(tracker['tag'])}", 8),
|
||||
self.config.loglevel,
|
||||
)
|
||||
body += logger.print_line(logger.insert_space(f'Tracker: {tracker["url"]}', 8), self.config.loglevel)
|
||||
body += logger.print_line(logger.insert_space(f"Tracker: {tracker['url']}", 8), self.config.loglevel)
|
||||
if not self.config.dry_run:
|
||||
torrent.add_tags(tracker["tag"])
|
||||
category = self.qbt.get_category(torrent.save_path)[0] if torrent.category == "" else torrent.category
|
||||
|
|
|
@ -85,7 +85,7 @@ class MyLogger:
|
|||
def _formatter(self, handler=None, border=True, log_only=False, space=False):
|
||||
"""Format log message"""
|
||||
console = f"| %(message)-{self.screen_width - 2}s |" if border else f"%(message)-{self.screen_width - 2}s"
|
||||
file = f"{' '*65}" if space else "[%(asctime)s] %(filename)-27s %(levelname)-10s "
|
||||
file = f"{' ' * 65}" if space else "[%(asctime)s] %(filename)-27s %(levelname)-10s "
|
||||
handlers = [handler] if handler else self._logger.handlers
|
||||
for h in handlers:
|
||||
if not log_only or isinstance(h, RotatingFileHandler):
|
||||
|
|
|
@ -390,7 +390,7 @@ class Qbt:
|
|||
self.config.data["tracker"][default_tag]["tag"] = [default_tag]
|
||||
except Exception:
|
||||
self.config.data["tracker"][default_tag] = {"tag": [default_tag]}
|
||||
e = f'No tags matched for {tracker["url"]}. Please check your config.yml file. Setting tag to {default_tag}'
|
||||
e = f"No tags matched for {tracker['url']}. Please check your config.yml file. Setting tag to {default_tag}"
|
||||
self.config.notify(e, "Tag", False)
|
||||
logger.warning(e)
|
||||
return tracker
|
||||
|
@ -448,8 +448,10 @@ class Qbt:
|
|||
else:
|
||||
recycle_path = self.config.recycle_dir
|
||||
# Create recycle bin if not exists
|
||||
torrent_path = os.path.join(recycle_path, "torrents") # Export torrent/fastresume from BT_backup
|
||||
torrent_export_path = os.path.join(recycle_path, "torrents_export") # Exported torrent file (qbittorrent v4.5.0+)
|
||||
# Export torrent/fastresume from BT_backup
|
||||
torrent_path = os.path.join(recycle_path, "torrents")
|
||||
# Exported torrent file (qbittorrent v4.5.0+)
|
||||
torrent_export_path = os.path.join(recycle_path, "torrents_export")
|
||||
torrents_json_path = os.path.join(recycle_path, "torrents_json")
|
||||
torrent_name = info["torrents"][0]
|
||||
torrent_exportable = self.current_version >= "v4.5.0"
|
||||
|
@ -472,7 +474,8 @@ class Qbt:
|
|||
dot_torrent_files = []
|
||||
# Exporting torrent via Qbit API (v4.5.0+)
|
||||
if torrent_exportable:
|
||||
hash_suffix = f"{info_hash[-8:]}" # Get the last 8 hash characters of the torrent
|
||||
# Get the last 8 hash characters of the torrent
|
||||
hash_suffix = f"{info_hash[-8:]}"
|
||||
torrent_export_file = os.path.join(torrent_export_path, f"{torrent_name} [{hash_suffix}].torrent")
|
||||
truncated_torrent_export_file = util.truncate_filename(torrent_export_file, offset=11)
|
||||
try:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
"""qBittorrent Manager."""
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import math
|
||||
|
@ -27,8 +28,8 @@ current_version = sys.version_info
|
|||
|
||||
if current_version < (REQUIRED_VERSION):
|
||||
print(
|
||||
"Version Error: Version: %s.%s.%s incompatible with qbit_manage please use Python %s+"
|
||||
% (current_version[0], current_version[1], current_version[2], REQUIRED_VERSION_STR)
|
||||
f"Version Error: Version: {current_version[0]}.{current_version[1]}.{current_version[2]} incompatible with "
|
||||
f"qbit_manage please use Python {REQUIRED_VERSION_STR}+"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
@ -450,7 +451,8 @@ def start():
|
|||
end_time = datetime.now()
|
||||
run_time = str(end_time - start_time).split(".", maxsplit=1)[0]
|
||||
if run is False:
|
||||
if is_valid_cron_syntax(sch): # Simple check to guess if it's a cron syntax
|
||||
# Simple check to guess if it's a cron syntax
|
||||
if is_valid_cron_syntax(sch):
|
||||
next_run_time = schedule_from_cron(sch)
|
||||
else:
|
||||
delta = timedelta(minutes=sch)
|
||||
|
@ -652,7 +654,8 @@ if __name__ == "__main__":
|
|||
logger.info(run_mode_message)
|
||||
start_loop(True)
|
||||
else:
|
||||
if is_valid_cron_syntax(sch): # Simple check to guess if it's a cron syntax
|
||||
# Simple check to guess if it's a cron syntax
|
||||
if is_valid_cron_syntax(sch):
|
||||
run_mode_message = f" Scheduled Mode: Running cron '{sch}'"
|
||||
next_run_time = schedule_from_cron(sch)
|
||||
next_run = calc_next_run(next_run_time)
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
flake8==7.1.2
|
||||
|
||||
pre-commit==4.1.0
|
||||
ruff==0.10.0
|
||||
|
|
22
ruff.toml
Normal file
22
ruff.toml
Normal file
|
@ -0,0 +1,22 @@
|
|||
line-length = 130
|
||||
|
||||
[lint]
|
||||
select = [
|
||||
"I", # isort - import order
|
||||
"UP", # pyupgrade
|
||||
"T10", # debugger
|
||||
"E", # pycodestyle errors
|
||||
"W", # pycodestyle warnings
|
||||
"F", # pyflakes
|
||||
]
|
||||
|
||||
ignore = [
|
||||
"E722", # E722 Do not use bare except, specify exception instead
|
||||
"E402", # E402 module level import not at top of file
|
||||
]
|
||||
|
||||
[lint.isort]
|
||||
force-single-line = true
|
||||
|
||||
[format]
|
||||
line-ending = "auto"
|
|
@ -14,16 +14,21 @@ import qbittorrentapi
|
|||
"""===Config==="""
|
||||
# qBittorrent WebUi Login
|
||||
qbt_login = {"host": "localhost", "port": 8080, "username": "???", "password": "???"}
|
||||
PATH = "M:" # Path of drive to monitor. Only torrents with paths that start with this may be deleted.
|
||||
# Path of drive to monitor. Only torrents with paths that start with this may be deleted.
|
||||
PATH = "M:"
|
||||
MIN_FREE_SPACE = 10 # In GB. Min free space on drive.
|
||||
MIN_FREE_USAGE = 0 # In decimal percentage, 0 to 1. Min % free space on drive.
|
||||
MIN_TORRENT_SHARE_RATIO = 0 # In decimal percentage, 0 to inf. Min seeding ratio of torrent to delete.
|
||||
MIN_TORRENT_AGE = 30 # In days, min age of torrent to delete. Uses seeding time.
|
||||
# In decimal percentage, 0 to inf. Min seeding ratio of torrent to delete.
|
||||
MIN_TORRENT_SHARE_RATIO = 0
|
||||
# In days, min age of torrent to delete. Uses seeding time.
|
||||
MIN_TORRENT_AGE = 30
|
||||
ALLOW_INCOMPLETE_TORRENT_DELETIONS = (
|
||||
False # Also delete torrents that haven't finished downloading. MIN_TORRENT_AGE now based on time active.
|
||||
# Also delete torrents that haven't finished downloading. MIN_TORRENT_AGE now based on time active.
|
||||
False
|
||||
)
|
||||
PREFER_PRIVATE_TORRENTS = (
|
||||
True # Will delete public torrents before private ones regardless of seed difference. See is_torrent_public().
|
||||
# Will delete public torrents before private ones regardless of seed difference. See is_torrent_public().
|
||||
True
|
||||
)
|
||||
"""===End Config==="""
|
||||
|
||||
|
@ -171,7 +176,8 @@ def main():
|
|||
qbt_client.torrents_delete(torrent_hashes=torrent_hash, delete_files=True)
|
||||
deleted_torrents.append(torrent_name)
|
||||
print(f"--- {torrent_name}")
|
||||
time.sleep(1) # Sleep a bit after each deletion to make sure disk usage is updated.
|
||||
# Sleep a bit after each deletion to make sure disk usage is updated.
|
||||
time.sleep(1)
|
||||
|
||||
# Print results
|
||||
print_free_space()
|
||||
|
|
|
@ -9,7 +9,8 @@ qbt_host = "qbittorrent:8080"
|
|||
qbt_user = None
|
||||
qbt_pass = None
|
||||
OLD_TRACKER = "https://blutopia.xyz" # This is the tracker you want to replace
|
||||
NEW_TRACKER = "https://blutopia.cc" # This is the tracker you want to replace it with
|
||||
# This is the tracker you want to replace it with
|
||||
NEW_TRACKER = "https://blutopia.cc"
|
||||
# --DEFINE VARIABLES--#
|
||||
# --START SCRIPT--#
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@ fi
|
|||
if git diff --cached --name-only | grep -q "VERSION"; then
|
||||
echo "The VERSION file is already modified. Skipping version update."
|
||||
exit 0
|
||||
elif git diff --name-only | grep -q "VERSION"; then
|
||||
echo "The VERSION file has unstaged changes. Please stage them before committing."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Read the current version from the VERSION file
|
||||
|
|
15
tox.ini
15
tox.ini
|
@ -1,5 +1,5 @@
|
|||
[tox]
|
||||
envlist = py39,py310,py311,py312,pre-commit
|
||||
envlist = py39,py310,py311,py312,py313,pre-commit
|
||||
skip_missing_interpreters = true
|
||||
tox_pip_extensions_ext_pip_custom_platform = true
|
||||
tox_pip_extensions_ext_venv_update = true
|
||||
|
@ -27,13 +27,6 @@ commands =
|
|||
pre-commit install -f --install-hooks
|
||||
pre-commit run --all-files
|
||||
|
||||
[flake8]
|
||||
max-line-length = 130
|
||||
|
||||
[pep8]
|
||||
extend-ignore = E722,E402
|
||||
|
||||
[tool.isort]
|
||||
add_imports = ["from __future__ import annotations"]
|
||||
force_single_line = true
|
||||
profile = "black"
|
||||
[testenv:ruff]
|
||||
deps = ruff
|
||||
commands = ruff check .
|
||||
|
|
Loading…
Add table
Reference in a new issue