qbit_manage/modules/core/remove_orphaned.py
bobokun 99cfac58aa
4.1.16 (#741)
* 4.1.16-develop1

* Update SUPPORTED_VERSIONS.json for master (#724)

* 4.1.15-develop1

* Update SUPPORTED_VERSIONS.json for master (#706)

* 4.1.14-develop1

* Update SUPPORTED_VERSIONS.json for master (#692)

* 4.1.13-develop

* Bump croniter from 3.0.3 to 3.0.4 (#680)

Bumps [croniter](https://github.com/kiorky/croniter) from 3.0.3 to 3.0.4.
- [Changelog](https://github.com/kiorky/croniter/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/kiorky/croniter/compare/3.0.3...3.0.4)

---
updated-dependencies:
- dependency-name: croniter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump qbittorrent-api from 2024.9.67 to 2024.10.68 (#684)

Bumps [qbittorrent-api](https://github.com/rmartin16/qbittorrent-api) from 2024.9.67 to 2024.10.68.
- [Release notes](https://github.com/rmartin16/qbittorrent-api/releases)
- [Changelog](https://github.com/rmartin16/qbittorrent-api/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rmartin16/qbittorrent-api/compare/v2024.9.67...v2024.10.68)

---
updated-dependencies:
- dependency-name: qbittorrent-api
  dependency-type: direct:production
  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>

* Update SUPPORTED_VERSIONS.json

* bump develop

* (ci): add ci for version bump on develop (#688)

* Fixes bug in torrent exporting for qbit versions under 4.5.0

* Bump actions/checkout from 3 to 4 (#690)

* Bump actions/setup-python from 3 to 5 (#689)

* Bump croniter from 3.0.4 to 5.0.1 (#685)

* [pre-commit.ci] pre-commit autoupdate (#682)

* 4.1.13

* Update SUPPORTED_VERSIONS.json

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: bobokun <jon.cy.lee98@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
Co-authored-by: bobokun <12660469+bobokun@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* chore(docs): Sync wiki to docs [skip-cd]

* Bump qbittorrent-api from 2024.10.68 to 2024.11.69 (#693)

* Bump qbittorrent-api from 2024.10.68 to 2024.11.69

Bumps [qbittorrent-api](https://github.com/rmartin16/qbittorrent-api) from 2024.10.68 to 2024.11.69.
- [Release notes](https://github.com/rmartin16/qbittorrent-api/releases)
- [Changelog](https://github.com/rmartin16/qbittorrent-api/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rmartin16/qbittorrent-api/compare/v2024.10.68...v2024.11.69)

---
updated-dependencies:
- dependency-name: qbittorrent-api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* bump develop (#694)

* chore: bump qbittorrent-api (#697)

* chore: bump qbittorrent-api
* Update VERSION

* Update SUPPORTED_VERSIONS.json

* chore(docs): Sync wiki to docs [skip-cd]

* 4.1.14

* Update SUPPORTED_VERSIONS.json

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: bobokun <jon.cy.lee98@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
Co-authored-by: bobokun <12660469+bobokun@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Actionbot <actions@github.com>

* (fix): BHD add additional unregistered messages for season packs (#707)

* (fix): BHD add additional unregistered messages for season packs

* Update VERSION

* docs: update README.md (#708)

* Update README.md

Fixed small typo

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* (bhd): handle additional season pack unregistered (#709)

* (bhd): handle additional season pack unregistered

"COMPLETE SEASON UPLOADED" did not match 

Complete Season Uploaded: https://beyond-hd.me/torrents/[link]

* fixup!

* Bump qbittorrent-api from 2024.11.70 to 2024.12.71 (#712)

Bumps [qbittorrent-api](https://github.com/rmartin16/qbittorrent-api) from 2024.11.70 to 2024.12.71.
- [Release notes](https://github.com/rmartin16/qbittorrent-api/releases)
- [Changelog](https://github.com/rmartin16/qbittorrent-api/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rmartin16/qbittorrent-api/compare/v2024.11.70...v2024.12.71)

---
updated-dependencies:
- dependency-name: qbittorrent-api
  dependency-type: direct:production
  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>

* bump 4.1.15-develop4

* Update SUPPORTED_VERSIONS.json

* Bump croniter from 5.0.1 to 6.0.0 (#711)

* Fix help description for --rem-orphaned argument (#716)

* Bump ruamel-yaml from 0.18.6 to 0.18.7 (#720)

Bumps ruamel-yaml from 0.18.6 to 0.18.7.

---
updated-dependencies:
- dependency-name: ruamel-yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump ruamel-yaml from 0.18.7 to 0.18.8 (#721)

Bumps ruamel-yaml from 0.18.7 to 0.18.8.

---
updated-dependencies:
- dependency-name: ruamel-yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump gitpython from 3.1.43 to 3.1.44 (#722)

Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.43 to 3.1.44.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](https://github.com/gitpython-developers/GitPython/compare/3.1.43...3.1.44)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [pre-commit.ci] pre-commit autoupdate (#715)

* [pre-commit.ci] pre-commit autoupdate

updates:
- [github.com/asottile/pyupgrade: v3.19.0 → v3.19.1](https://github.com/asottile/pyupgrade/compare/v3.19.0...v3.19.1)

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: bobokun <12660469+bobokun@users.noreply.github.com>

* Fixes #719

* 4.1.15

* Update SUPPORTED_VERSIONS.json

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: bobokun <jon.cy.lee98@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
Co-authored-by: bobokun <12660469+bobokun@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Actionbot <actions@github.com>
Co-authored-by: tanka8 <tanka8@gmail.com>
Co-authored-by: Michael Brünen <34708235+OddMagnet@users.noreply.github.com>

* Adds #695

* Bump ruamel-yaml from 0.18.8 to 0.18.9 (#725)

Bumps ruamel-yaml from 0.18.8 to 0.18.9.

---
updated-dependencies:
- dependency-name: ruamel-yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump ruamel-yaml from 0.18.9 to 0.18.10 (#727)

Bumps ruamel-yaml from 0.18.9 to 0.18.10.

---
updated-dependencies:
- dependency-name: ruamel-yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* log orphaned files detected in info instead of debug

* Bump pre-commit from 4.0.1 to 4.1.0 (#735)

Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 4.0.1 to 4.1.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v4.0.1...v4.1.0)

---
updated-dependencies:
- dependency-name: pre-commit
  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>

* Bump dependabot/fetch-metadata from 2.2.0 to 2.3.0 (#739)

Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](https://github.com/dependabot/fetch-metadata/compare/v2.2.0...v2.3.0)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-type: direct:production
  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>

* [pre-commit.ci] pre-commit autoupdate (#734)

updates:
- [github.com/hhatto/autopep8: v2.3.1 → v2.3.2](https://github.com/hhatto/autopep8/compare/v2.3.1...v2.3.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

* 4.1.16

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Actionbot <actions@github.com>
Co-authored-by: tanka8 <tanka8@gmail.com>
Co-authored-by: Michael Brünen <34708235+OddMagnet@users.noreply.github.com>
2025-02-02 18:12:20 -05:00

146 lines
6.3 KiB
Python

import os
from concurrent.futures import ThreadPoolExecutor
from fnmatch import fnmatch
from modules import util
logger = util.logger
class RemoveOrphaned:
def __init__(self, qbit_manager):
self.qbt = qbit_manager
self.config = qbit_manager.config
self.client = qbit_manager.client
self.stats = 0
self.remote_dir = qbit_manager.config.remote_dir
self.root_dir = qbit_manager.config.root_dir
self.orphaned_dir = qbit_manager.config.orphaned_dir
max_workers = max(os.cpu_count() - 1, 1)
self.executor = ThreadPoolExecutor(max_workers=max_workers)
self.rem_orphaned()
self.executor.shutdown()
def rem_orphaned(self):
"""Remove orphaned files from remote directory"""
self.stats = 0
logger.separator("Checking for Orphaned Files", space=False, border=False)
torrent_files = []
orphaned_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)
# Get an updated list of torrents
logger.print_line("Locating orphan files", self.config.loglevel)
torrent_list = self.qbt.get_torrents({"sort": "added_on"})
torrent_files.extend(
[
fullpath
for fullpathlist in self.executor.map(self.get_full_path_of_torrent_files, torrent_list)
for fullpath in fullpathlist
]
)
root_files_set = set(root_files.result())
torrent_files_set = set(torrent_files)
orphaned_files = root_files_set - torrent_files_set
if self.config.orphaned["exclude_patterns"]:
logger.print_line("Processing orphan exclude patterns")
exclude_patterns = [
exclude_pattern.replace(self.remote_dir, self.root_dir)
for exclude_pattern in self.config.orphaned["exclude_patterns"]
]
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
max_orphaned_files_to_delete = self.config.orphaned.get("max_orphaned_files_to_delete")
if len(orphaned_files) and len(orphaned_files) > max_orphaned_files_to_delete and max_orphaned_files_to_delete != -1:
e = (
f"Too many orphaned files detected ({len(orphaned_files)}). "
f"Max Threshold for deletion is set to {max_orphaned_files_to_delete}. "
"Aborting deletion to avoid accidental data loss."
)
self.config.notify(e, "Remove Orphaned", False)
logger.info(f"Orphaned files detected: {orphaned_files}")
logger.warning(e)
return
elif orphaned_files:
orphaned_files = sorted(orphaned_files)
os.makedirs(self.orphaned_dir, exist_ok=True)
body = []
num_orphaned = len(orphaned_files)
logger.print_line(f"{num_orphaned} Orphaned files found", self.config.loglevel)
body += logger.print_line("\n".join(orphaned_files), self.config.loglevel)
if self.config.orphaned["empty_after_x_days"] == 0:
body += logger.print_line(
f"{'Not Deleting' if self.config.dry_run else 'Deleting'} {num_orphaned} Orphaned files",
self.config.loglevel,
)
else:
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)}",
self.config.loglevel,
)
attr = {
"function": "rem_orphaned",
"title": f"Removing {num_orphaned} Orphaned Files",
"body": "\n".join(body),
"orphaned_files": list(orphaned_files),
"orphaned_directory": self.orphaned_dir.replace(self.remote_dir, self.root_dir),
"total_orphaned_files": num_orphaned,
}
self.config.send_notifications(attr)
# Delete empty directories after moving orphan files
if not self.config.dry_run:
orphaned_parent_path = set(self.executor.map(self.handle_orphaned_files, orphaned_files))
logger.print_line("Removing newly empty directories", self.config.loglevel)
self.executor.map(
lambda directory: util.remove_empty_directories(
directory, self.qbt.get_category_save_paths(), exclude_patterns
),
orphaned_parent_path,
)
else:
logger.print_line("No Orphaned Files found.", self.config.loglevel)
def handle_orphaned_files(self, file):
src = file.replace(self.root_dir, self.remote_dir)
dest = os.path.join(self.orphaned_dir, file.replace(self.root_dir, ""))
orphaned_parent_path = os.path.dirname(file).replace(self.root_dir, self.remote_dir)
"""Delete orphaned files directly if empty_after_x_days is set to 0"""
if self.config.orphaned["empty_after_x_days"] == 0:
try:
util.delete_files(src)
except Exception:
logger.error(f"Error deleting orphaned file: {file}")
util.move_files(src, dest, True)
else: # Move orphaned files to orphaned directory
util.move_files(src, dest, True)
return orphaned_parent_path
def get_full_path_of_torrent_files(self, torrent):
torrent_files = map(lambda dict: dict.name, torrent.files)
save_path = torrent.save_path
fullpath_torrent_files = []
for file in torrent_files:
fullpath = os.path.join(save_path, file)
# Replace fullpath with \\ if qbm is running in docker (linux) but qbt is on windows
fullpath = fullpath.replace(r"/", "\\") if ":\\" in fullpath else fullpath
fullpath_torrent_files.append(fullpath)
return fullpath_torrent_files