Merge pull request #677 from StuffAnThings/develop

4.1.12
This commit is contained in:
bobokun 2024-10-19 09:48:02 -04:00 committed by GitHub
commit 00ce5c6c30
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 91 additions and 23 deletions

View file

@ -1,7 +1,7 @@
--- ---
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0 rev: v5.0.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
@ -38,12 +38,12 @@ repos:
name: isort (python) name: isort (python)
args: [--force-single-line-imports, --profile, black] args: [--force-single-line-imports, --profile, black]
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v3.17.0 rev: v3.18.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py3-plus] args: [--py3-plus]
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 24.8.0 rev: 24.10.0
hooks: hooks:
- id: black - id: black
language_version: python3 language_version: python3
@ -60,4 +60,4 @@ repos:
entry: ./scripts/pre-commit/increase_version.sh entry: ./scripts/pre-commit/increase_version.sh
language: script language: script
pass_filenames: false pass_filenames: false
stages: [commit] stages: [pre-commit]

View file

@ -1,11 +1,11 @@
# Requirements Updated
humanize==4.11.0
# New Updates # New Updates
- Adds new config option `disable_qbt_default_share_limits` to allow qbit_manage to handle share limits and disable qbittorrent's default share limits - Adds new script to remove cross-seed tag (`scripts/remove_cross-seed_tag.py`)
- Adds new config option `max_orphaned_files_to_delete` to set default safeguards against mass deletion when running remove orphaned.
- Adds new environment variables `QBT_LOG_SIZE` and `QBT_LOG_COUNT` to customize log retention (Closes #656)
# Bug Fixes # Bug Fixes
- Truncates Recyclebin JSON filename when its too long. (Closes #604) - List orphaned files when reaches max threshold. (Closes #672)
- Uses Qbittorrent's torrent export to save .torrent files for qbittorrent version > 4.5.0 (Closes #650) - Removing empty directories now ignores exclude patterns (Closes #624)
- Include orphaned files and recycle bin in the list of folders to ignore when looking for noHL (Closes #660)
**Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.1.10...v4.1.11 **Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.1.11...v4.1.12

View file

@ -1,7 +1,7 @@
{ {
"master": { "master": {
"qbit": "v4.6.6", "qbit": "v5.0.0",
"qbitapi": "2024.8.65" "qbitapi": "2024.9.67"
}, },
"develop": { "develop": {
"qbit": "v5.0.0", "qbit": "v5.0.0",

View file

@ -1 +1 @@
4.1.11 4.1.12

View file

@ -29,7 +29,8 @@ class RemoveOrphaned:
logger.separator("Checking for Orphaned Files", space=False, border=False) logger.separator("Checking for Orphaned Files", space=False, border=False)
torrent_files = [] torrent_files = []
orphaned_files = [] orphaned_files = []
excluded_orphan_files = [] excluded_orphan_files = set()
exclude_patterns = []
root_files = self.executor.submit(util.get_root_files, self.root_dir, self.remote_dir, self.orphaned_dir) root_files = self.executor.submit(util.get_root_files, self.root_dir, self.remote_dir, self.orphaned_dir)
@ -54,11 +55,13 @@ class RemoveOrphaned:
exclude_pattern.replace(self.remote_dir, self.root_dir) exclude_pattern.replace(self.remote_dir, self.root_dir)
for exclude_pattern in self.config.orphaned["exclude_patterns"] for exclude_pattern in self.config.orphaned["exclude_patterns"]
] ]
excluded_orphan_files = [
file for file in orphaned_files for exclude_pattern in exclude_patterns if fnmatch(file, exclude_pattern)
]
orphaned_files = set(orphaned_files) - set(excluded_orphan_files) for file in orphaned_files:
for exclude_pattern in exclude_patterns:
if fnmatch(file, exclude_pattern):
excluded_orphan_files.add(file)
orphaned_files = orphaned_files - excluded_orphan_files
# Check the threshold before deleting orphaned files # Check the threshold before deleting orphaned files
max_orphaned_files_to_delete = self.config.orphaned.get("max_orphaned_files_to_delete") max_orphaned_files_to_delete = self.config.orphaned.get("max_orphaned_files_to_delete")
@ -69,6 +72,7 @@ class RemoveOrphaned:
"Aborting deletion to avoid accidental data loss." "Aborting deletion to avoid accidental data loss."
) )
self.config.notify(e, "Remove Orphaned", False) self.config.notify(e, "Remove Orphaned", False)
logger.debug(f"Orphaned files detected: {orphaned_files}")
logger.warning(e) logger.warning(e)
return return
elif orphaned_files: elif orphaned_files:
@ -104,7 +108,9 @@ class RemoveOrphaned:
orphaned_parent_path = set(self.executor.map(self.handle_orphaned_files, orphaned_files)) orphaned_parent_path = set(self.executor.map(self.handle_orphaned_files, orphaned_files))
logger.print_line("Removing newly empty directories", self.config.loglevel) logger.print_line("Removing newly empty directories", self.config.loglevel)
self.executor.map( self.executor.map(
lambda directory: util.remove_empty_directories(directory, self.qbt.get_category_save_paths()), lambda directory: util.remove_empty_directories(
directory, self.qbt.get_category_save_paths(), exclude_patterns
),
orphaned_parent_path, orphaned_parent_path,
) )

View file

@ -6,6 +6,7 @@ import os
import shutil import shutil
import signal import signal
import time import time
from fnmatch import fnmatch
from pathlib import Path from pathlib import Path
import requests import requests
@ -486,7 +487,7 @@ def copy_files(src, dest):
logger.error(ex) logger.error(ex)
def remove_empty_directories(pathlib_root_dir, excluded_paths=None): def remove_empty_directories(pathlib_root_dir, excluded_paths=None, exclude_patterns=[]):
"""Remove empty directories recursively, optimized version.""" """Remove empty directories recursively, optimized version."""
pathlib_root_dir = Path(pathlib_root_dir) pathlib_root_dir = Path(pathlib_root_dir)
if excluded_paths is not None: if excluded_paths is not None:
@ -499,6 +500,14 @@ def remove_empty_directories(pathlib_root_dir, excluded_paths=None):
if excluded_paths and root_path in excluded_paths: if excluded_paths and root_path in excluded_paths:
continue continue
exclude_pattern_match = False
for exclude_pattern in exclude_patterns:
if fnmatch(os.path.join(root, ""), exclude_pattern):
exclude_pattern_match = True
break
if exclude_pattern_match:
continue
# Attempt to remove the directory if it's empty # Attempt to remove the directory if it's empty
try: try:
os.rmdir(root) os.rmdir(root)

View file

@ -1,2 +1,2 @@
flake8==7.1.1 flake8==7.1.1
pre-commit==3.8.0 pre-commit==4.0.1

View file

@ -1,7 +1,7 @@
bencodepy==0.9.5 bencodepy==0.9.5
croniter==3.0.3 croniter==3.0.3
GitPython==3.1.43 GitPython==3.1.43
humanize==4.10.0 humanize==4.11.0
pytimeparse2==1.7.1 pytimeparse2==1.7.1
qbittorrent-api==2024.9.67 qbittorrent-api==2024.9.67
requests==2.32.3 requests==2.32.3

View file

@ -0,0 +1,53 @@
#!/usr/bin/env python3
import os
# USES ENVIRONMENTAL VARIABLES, IF NONE ARE PRESENT WILL FALLBACK TO THE SECOND STRING
QBIT_HOST = os.getenv("QBT_HOST", "http://localhost:8080")
QBIT_USERNAME = os.getenv("QBT_USERNAME", "admin")
QBIT_PASSWORD = os.getenv("QBT_PASSWORD", "YOURPASSWORD")
CRED = "\033[91m"
CGREEN = "\33[32m"
CEND = "\033[0m"
CROSS_SEED_TAG = "cross-seed"
def split(separator, data):
if data is None:
return None
else:
return [item.strip() for item in str(data).split(separator)]
try:
from qbittorrentapi import APIConnectionError
from qbittorrentapi import Client
from qbittorrentapi import LoginFailed
except ModuleNotFoundError:
print('Error: qbittorrent-api not installed. Please install using the command "pip install qbittorrent-api"')
exit(1)
try:
qbt_client = Client(host=QBIT_HOST, username=QBIT_USERNAME, password=QBIT_PASSWORD)
except LoginFailed:
raise "Qbittorrent Error: Failed to login. Invalid username/password."
except APIConnectionError:
raise "Qbittorrent Error: Unable to connect to the client."
except Exception:
raise "Qbittorrent Error: Unable to connect to the client."
print("qBittorrent:", qbt_client.app_version())
print("qBittorrent Web API:", qbt_client.app_web_api_version())
print()
torrents_list = qbt_client.torrents.info(sort="added_on", reverse=True)
print("Total torrents:", len(torrents_list))
print()
for torrent in torrents_list:
torrent_tags = split(",", torrent.tags)
if CROSS_SEED_TAG in torrent_tags:
print(CGREEN, "remove cross-seed tag:", torrent.name, CEND)
torrent.remove_tags(tags=CROSS_SEED_TAG)