mirror of
https://github.com/StuffAnThings/qbit_manage.git
synced 2024-09-20 15:26:02 +08:00
commit
d01afe9603
|
@ -19,7 +19,7 @@ jobs:
|
|||
# will not occur.
|
||||
- name: Dependabot metadata
|
||||
id: dependabot-metadata
|
||||
uses: dependabot/fetch-metadata@v1.5.1
|
||||
uses: dependabot/fetch-metadata@v1.6.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
# Here the PR gets approved.
|
||||
|
|
|
@ -27,7 +27,7 @@ repos:
|
|||
args: [--format, parsable, --strict]
|
||||
exclude: ^.github/
|
||||
- repo: https://github.com/lyz-code/yamlfix
|
||||
rev: 1.11.0
|
||||
rev: 1.13.0
|
||||
hooks:
|
||||
- id: yamlfix
|
||||
exclude: ^.github/
|
||||
|
@ -36,18 +36,18 @@ repos:
|
|||
hooks:
|
||||
- id: reorder-python-imports
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.7.0
|
||||
rev: v3.10.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py3-plus]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.3.0
|
||||
rev: 23.7.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3
|
||||
args: [--line-length, '130', --preview]
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 6.0.0
|
||||
rev: 6.1.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
args: [--config=.flake8]
|
||||
|
|
17
CHANGELOG
17
CHANGELOG
|
@ -1,13 +1,12 @@
|
|||
# Requirements Updated
|
||||
- qbitorrent-api updated to 2023.6.50
|
||||
- ruamel.yaml updated to 0.17.32
|
||||
# New Features
|
||||
- Adds min_num_seeds condition to share_limits (Closes [#321](https://github.com/StuffAnThings/qbit_manage/issues/321))
|
||||
- qbitorrent-api updated to 2023.7.52
|
||||
|
||||
# Bug Fixes
|
||||
Fixes [#333](https://github.com/StuffAnThings/qbit_manage/issues/333)
|
||||
Fixes [#340](https://github.com/StuffAnThings/qbit_manage/issues/340)
|
||||
Fixes [#343](https://github.com/StuffAnThings/qbit_manage/issues/343)
|
||||
Fixes bug when checking tags in torrents
|
||||
- Fixes a few webhook bugs with Notifiarr
|
||||
- Fixes [#355](https://github.com/StuffAnThings/qbit_manage/issues/355)
|
||||
- Fixes [#356](https://github.com/StuffAnThings/qbit_manage/issues/356)
|
||||
- Fixes [#363](https://github.com/StuffAnThings/qbit_manage/issues/363)
|
||||
- Fixes [#370](https://github.com/StuffAnThings/qbit_manage/issues/370)
|
||||
|
||||
**Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.0.1...v4.0.2
|
||||
|
||||
**Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.0.2...v4.0.3
|
||||
|
|
|
@ -663,10 +663,8 @@ class Config:
|
|||
if empty_after_x_days <= days:
|
||||
num_del += 1
|
||||
body += logger.print_line(
|
||||
(
|
||||
f"{'Did not delete' if self.dry_run else 'Deleted'} "
|
||||
f"{filename} from {folder} (Last modified {round(days)} days ago)."
|
||||
),
|
||||
f"{filename} from {folder} (Last modified {round(days)} days ago).",
|
||||
self.loglevel,
|
||||
)
|
||||
files += [str(filename)]
|
||||
|
@ -679,10 +677,8 @@ class Config:
|
|||
for path in location_path_list:
|
||||
util.remove_empty_directories(path, "**/*")
|
||||
body += logger.print_line(
|
||||
(
|
||||
f"{'Did not delete' if self.dry_run else 'Deleted'} {num_del} files "
|
||||
f"({util.human_readable_size(size_bytes)}) from the {location}."
|
||||
),
|
||||
f"({util.human_readable_size(size_bytes)}) from the {location}.",
|
||||
self.loglevel,
|
||||
)
|
||||
attr = {
|
||||
|
|
|
@ -128,7 +128,7 @@ class CrossSeed:
|
|||
"torrents": [t_name],
|
||||
"torrent_category": t_cat,
|
||||
"torrent_tag": "cross-seed",
|
||||
"torrent_tracker": tracker,
|
||||
"torrent_tracker": tracker["url"],
|
||||
}
|
||||
self.notify_attr.append(attr)
|
||||
self.torrents_updated.append(t_name)
|
||||
|
|
|
@ -46,7 +46,7 @@ class ReCheck:
|
|||
"title": "Resuming Torrent",
|
||||
"body": body,
|
||||
"torrents": [t_name],
|
||||
"torrent_tag": tracker["tag"],
|
||||
"torrent_tag": ", ".join(tracker["tag"]),
|
||||
"torrent_category": t_category,
|
||||
"torrent_tracker": tracker["url"],
|
||||
"notifiarr_indexer": tracker["notifiarr"],
|
||||
|
@ -64,10 +64,8 @@ class ReCheck:
|
|||
)
|
||||
logger.debug(
|
||||
logger.insert_space(
|
||||
(
|
||||
f"-- Seeding Time vs Max Seed Time: {timedelta(seconds=torrent.seeding_time)} < "
|
||||
f"{timedelta(minutes=torrent.max_seeding_time)}"
|
||||
),
|
||||
f"{timedelta(minutes=torrent.max_seeding_time)}",
|
||||
4,
|
||||
)
|
||||
)
|
||||
|
@ -95,7 +93,7 @@ class ReCheck:
|
|||
"title": "Resuming Torrent",
|
||||
"body": body,
|
||||
"torrents": [t_name],
|
||||
"torrent_tag": tracker["tag"],
|
||||
"torrent_tag": ", ".join(tracker["tag"]),
|
||||
"torrent_category": t_category,
|
||||
"torrent_tracker": tracker["url"],
|
||||
"notifiarr_indexer": tracker["notifiarr"],
|
||||
|
@ -120,7 +118,7 @@ class ReCheck:
|
|||
"title": "Rechecking Torrent",
|
||||
"body": body,
|
||||
"torrents": [t_name],
|
||||
"torrent_tag": tracker["tag"],
|
||||
"torrent_tag": ", ".join(tracker["tag"]),
|
||||
"torrent_category": t_category,
|
||||
"torrent_tracker": tracker["url"],
|
||||
"notifiarr_indexer": tracker["notifiarr"],
|
||||
|
|
|
@ -67,10 +67,8 @@ class RemoveOrphaned:
|
|||
logger.print_line(f"{num_orphaned} Orphaned files found", self.config.loglevel)
|
||||
body += logger.print_line("\n".join(orphaned_files), self.config.loglevel)
|
||||
body += logger.print_line(
|
||||
(
|
||||
f"{'Not moving' if self.config.dry_run else 'Moving'} {num_orphaned} Orphaned files "
|
||||
f"to {self.orphaned_dir.replace(self.remote_dir,self.root_dir)}"
|
||||
),
|
||||
f"to {self.orphaned_dir.replace(self.remote_dir,self.root_dir)}",
|
||||
self.config.loglevel,
|
||||
)
|
||||
|
||||
|
|
|
@ -142,28 +142,22 @@ class RemoveUnregistered:
|
|||
if self.stats_deleted >= 1 or self.stats_deleted_contents >= 1:
|
||||
if self.stats_deleted >= 1:
|
||||
logger.print_line(
|
||||
(
|
||||
f"{'Did not delete' if self.config.dry_run else 'Deleted'} {self.stats_deleted} "
|
||||
f".torrent{'s' if self.stats_deleted > 1 else ''} but not content files."
|
||||
),
|
||||
f".torrent{'s' if self.stats_deleted > 1 else ''} but not content files.",
|
||||
self.config.loglevel,
|
||||
)
|
||||
if self.stats_deleted_contents >= 1:
|
||||
logger.print_line(
|
||||
(
|
||||
f"{'Did not delete' if self.config.dry_run else 'Deleted'} {self.stats_deleted_contents} "
|
||||
f".torrent{'s' if self.stats_deleted_contents > 1 else ''} AND content files."
|
||||
),
|
||||
f".torrent{'s' if self.stats_deleted_contents > 1 else ''} AND content files.",
|
||||
self.config.loglevel,
|
||||
)
|
||||
else:
|
||||
logger.print_line("No unregistered torrents found.", self.config.loglevel)
|
||||
if self.stats_untagged >= 1:
|
||||
logger.print_line(
|
||||
(
|
||||
f"{'Did not delete' if self.config.dry_run else 'Deleted'} {self.tag_error} tags for {self.stats_untagged} "
|
||||
f".torrent{'s.' if self.stats_untagged > 1 else '.'}"
|
||||
),
|
||||
f".torrent{'s.' if self.stats_untagged > 1 else '.'}",
|
||||
self.config.loglevel,
|
||||
)
|
||||
if self.stats_tagged >= 1:
|
||||
|
|
|
@ -55,6 +55,7 @@ class ShareLimits:
|
|||
"torrent_max_ratio": group_config["max_ratio"],
|
||||
"torrent_max_seeding_time": group_config["max_seeding_time"],
|
||||
"torrent_min_seeding_time": group_config["min_seeding_time"],
|
||||
"torrent_min_num_seeds": group_config["min_num_seeds"],
|
||||
"torrent_limit_upload_speed": group_config["limit_upload_speed"],
|
||||
}
|
||||
if len(self.torrents_updated) > 0:
|
||||
|
@ -196,6 +197,7 @@ class ShareLimits:
|
|||
"Config Max Seeding Time vs Torrent Max Seeding Time: "
|
||||
f"{group_config['max_seeding_time']} vs {torrent.max_seeding_time}"
|
||||
)
|
||||
logger.trace(f"Config Min Num Seeds vs Torrent Num Seeds: {group_config['min_num_seeds']} vs {torrent.num_complete}")
|
||||
logger.trace(f"check_max_seeding_time: {check_max_seeding_time}")
|
||||
logger.trace(
|
||||
"Config Limit Upload Speed vs Torrent Limit Upload Speed: "
|
||||
|
@ -218,8 +220,6 @@ class ShareLimits:
|
|||
self.stats_tagged += 1
|
||||
self.torrents_updated.append(t_name)
|
||||
|
||||
# Cleanup torrents if the torrent meets the criteria for deletion and cleanup is enabled
|
||||
if group_config["cleanup"]:
|
||||
tor_reached_seed_limit = self.has_reached_seed_limit(
|
||||
torrent=torrent,
|
||||
max_ratio=group_config["max_ratio"],
|
||||
|
@ -229,6 +229,8 @@ class ShareLimits:
|
|||
resume_torrent=group_config["resume_torrent_after_change"],
|
||||
tracker=tracker["url"],
|
||||
)
|
||||
# Cleanup torrents if the torrent meets the criteria for deletion and cleanup is enabled
|
||||
if group_config["cleanup"]:
|
||||
if tor_reached_seed_limit:
|
||||
if t_hash not in self.tdel_dict:
|
||||
self.tdel_dict[t_hash] = {}
|
||||
|
@ -391,11 +393,9 @@ class ShareLimits:
|
|||
print_log += logger.print_line(logger.insert_space(f"Tracker: {tracker}", 8), self.config.loglevel)
|
||||
print_log += logger.print_line(
|
||||
logger.insert_space(
|
||||
(
|
||||
f"Min seed time not met: {timedelta(seconds=torrent.seeding_time)} <="
|
||||
f" {timedelta(minutes=min_seeding_time)}. Removing Share Limits so qBittorrent can continue"
|
||||
" seeding."
|
||||
),
|
||||
" seeding.",
|
||||
8,
|
||||
),
|
||||
self.config.loglevel,
|
||||
|
@ -423,11 +423,9 @@ class ShareLimits:
|
|||
print_log += logger.print_line(logger.insert_space(f"Tracker: {tracker}", 8), self.config.loglevel)
|
||||
print_log += logger.print_line(
|
||||
logger.insert_space(
|
||||
(
|
||||
f"Min number of seeds not met: Total Seeds ({torrent.num_complete}) < "
|
||||
f"min_num_seeds({min_num_seeds}). Removing Share Limits so qBittorrent can continue"
|
||||
" seeding."
|
||||
),
|
||||
" seeding.",
|
||||
8,
|
||||
),
|
||||
self.config.loglevel,
|
||||
|
@ -456,10 +454,8 @@ class ShareLimits:
|
|||
if seeding_time_limit:
|
||||
if (torrent.seeding_time >= seeding_time_limit * 60) and _has_reached_min_seeding_time_limit():
|
||||
body += logger.insert_space(
|
||||
(
|
||||
f"Seeding Time vs Max Seed Time: {timedelta(seconds=torrent.seeding_time)} >= "
|
||||
f"{timedelta(minutes=seeding_time_limit)}"
|
||||
),
|
||||
f"{timedelta(minutes=seeding_time_limit)}",
|
||||
8,
|
||||
)
|
||||
return True
|
||||
|
|
|
@ -120,20 +120,16 @@ class TagNoHardLinks:
|
|||
self.check_previous_nohardlinks_tagged_torrents(has_nohardlinks, torrent, tracker, category)
|
||||
if self.stats_tagged >= 1:
|
||||
logger.print_line(
|
||||
(
|
||||
f"{'Did not Tag' if self.config.dry_run else 'Added Tag'} for {self.stats_tagged} "
|
||||
f".torrent{'s.' if self.stats_tagged > 1 else '.'}"
|
||||
),
|
||||
f".torrent{'s.' if self.stats_tagged > 1 else '.'}",
|
||||
self.config.loglevel,
|
||||
)
|
||||
else:
|
||||
logger.print_line("No torrents to tag with no hardlinks.", self.config.loglevel)
|
||||
if self.stats_untagged >= 1:
|
||||
logger.print_line(
|
||||
(
|
||||
f"{'Did not delete' if self.config.dry_run else 'Deleted'} "
|
||||
f"{self.nohardlinks_tag} tags for {self.stats_untagged} "
|
||||
f".torrent{'s.' if self.stats_untagged > 1 else '.'}"
|
||||
),
|
||||
f".torrent{'s.' if self.stats_untagged > 1 else '.'}",
|
||||
self.config.loglevel,
|
||||
)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
from qbittorrentapi import APIConnectionError
|
||||
from qbittorrentapi import Client
|
||||
from qbittorrentapi import LoginFailed
|
||||
from qbittorrentapi import NotFound404Error
|
||||
|
@ -74,15 +73,10 @@ class Qbt:
|
|||
except LoginFailed as exc:
|
||||
ex = "Qbittorrent Error: Failed to login. Invalid username/password."
|
||||
self.config.notify(ex, "Qbittorrent")
|
||||
raise Failed(ex) from exc
|
||||
except APIConnectionError as exc:
|
||||
ex = "Qbittorrent Error: Unable to connect to the client."
|
||||
self.config.notify(ex, "Qbittorrent")
|
||||
raise Failed(ex) from exc
|
||||
raise Failed(exc) from exc
|
||||
except Exception as exc:
|
||||
ex = "Qbittorrent Error: Unable to connect to the client."
|
||||
self.config.notify(ex, "Qbittorrent")
|
||||
raise Failed(ex) from exc
|
||||
self.config.notify(exc, "Qbittorrent")
|
||||
raise Failed(exc) from exc
|
||||
logger.separator("Getting Torrent List", space=False, border=False)
|
||||
self.torrent_list = self.get_torrents({"sort": "added_on"})
|
||||
|
||||
|
|
|
@ -384,7 +384,18 @@ def move_files(src, dest, mod=False):
|
|||
shutil.move(src, dest)
|
||||
except PermissionError as perm:
|
||||
logger.warning(f"{perm} : Copying files instead.")
|
||||
try:
|
||||
shutil.copyfile(src, dest)
|
||||
except Exception as ex:
|
||||
logger.stacktrace()
|
||||
logger.error(ex)
|
||||
return to_delete
|
||||
if os.path.isfile(src):
|
||||
logger.warning(f"Removing original file: {src}")
|
||||
try:
|
||||
os.remove(src)
|
||||
except OSError as e:
|
||||
logger.warning(f"Error: {e.filename} - {e.strerror}.")
|
||||
to_delete = True
|
||||
except FileNotFoundError as file:
|
||||
logger.warning(f"{file} : source: {src} -> destination: {dest}")
|
||||
|
@ -501,12 +512,12 @@ class CheckHardLinks:
|
|||
continue
|
||||
file_size = os.stat(files).st_size
|
||||
file_no_hardlinks = os.stat(files).st_nlink
|
||||
logger.trace(f"Checking file: {file}")
|
||||
logger.trace(f"Checking file inum: {os.stat(file).st_ino}")
|
||||
logger.trace(f"Checking file: {files}")
|
||||
logger.trace(f"Checking file inum: {os.stat(files).st_ino}")
|
||||
logger.trace(f"Checking file size: {file_size}")
|
||||
logger.trace(f"Checking no of hard links: {file_no_hardlinks}")
|
||||
logger.trace(f"Checking inode_count dict: {self.inode_count.get(os.stat(file).st_ino)}")
|
||||
if file_no_hardlinks - self.inode_count.get(os.stat(file).st_ino, 1) > 0 and file_size >= (
|
||||
logger.trace(f"Checking inode_count dict: {self.inode_count.get(os.stat(files).st_ino)}")
|
||||
if file_no_hardlinks - self.inode_count.get(os.stat(files).st_ino, 1) > 0 and file_size >= (
|
||||
largest_file_size * threshold
|
||||
):
|
||||
check_for_hl = False
|
||||
|
|
|
@ -171,10 +171,8 @@ class Webhooks:
|
|||
def notify(self, torrents_updated=[], payload={}, group_by=""):
|
||||
if len(torrents_updated) > GROUP_NOTIFICATION_LIMIT:
|
||||
logger.trace(
|
||||
(
|
||||
f"Number of torrents updated > {GROUP_NOTIFICATION_LIMIT}, grouping notifications"
|
||||
f"{f' by {group_by}' if group_by else ''}"
|
||||
),
|
||||
f"{f' by {group_by}' if group_by else ''}",
|
||||
)
|
||||
if group_by == "category":
|
||||
group_attr = group_notifications_by_key(payload, "torrent_category")
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
flake8==6.0.0
|
||||
flake8==6.1.0
|
||||
pre-commit==3.3.3
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
bencodepy==0.9.5
|
||||
GitPython==3.1.32
|
||||
qbittorrent-api==2023.6.50
|
||||
qbittorrent-api==2023.7.52
|
||||
requests==2.31.0
|
||||
retrying==1.3.4
|
||||
ruamel.yaml==0.17.32
|
||||
|
|
Loading…
Reference in a new issue