mirror of
https://github.com/StuffAnThings/qbit_manage.git
synced 2025-10-09 13:29:15 +08:00
4.1.0 (#520)
* Sample solution. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update modules/config.py Increase efficiency by using a boolean in place of a functional check. Co-authored-by: bobokun <12660469+bobokun@users.noreply.github.com> * Bump softprops/action-gh-release from 1 to 2 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * update group_uplolad_speed logic * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Bump dependabot/fetch-metadata from 1.6.0 to 2.0.0 Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 1.6.0 to 2.0.0. - [Release notes](https://github.com/dependabot/fetch-metadata/releases) - [Commits](https://github.com/dependabot/fetch-metadata/compare/v1.6.0...v2.0.0) --- updated-dependencies: - dependency-name: dependabot/fetch-metadata dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * Bump pre-commit from 3.6.2 to 3.7.0 Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.6.2 to 3.7.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/v3.6.2...v3.7.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> * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/hhatto/autopep8: v2.0.4 → v2.1.0](https://github.com/hhatto/autopep8/compare/v2.0.4...v2.1.0) - [github.com/asottile/pyupgrade: v3.15.1 → v3.15.2](https://github.com/asottile/pyupgrade/compare/v3.15.1...v3.15.2) - [github.com/psf/black: 24.2.0 → 24.3.0](https://github.com/psf/black/compare/24.2.0...24.3.0) * Bump qbittorrent-api from 2024.2.59 to 2024.3.60 Bumps [qbittorrent-api](https://github.com/rmartin16/qbittorrent-api) from 2024.2.59 to 2024.3.60. - [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.2.59...v2024.3.60) --- updated-dependencies: - dependency-name: qbittorrent-api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Bump 4.0.10 * Revert "Add Aggregate Group Speed Capabilities" * Bump gitpython from 3.1.42 to 3.1.43 Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.42 to 3.1.43. - [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.42...3.1.43) --- updated-dependencies: - dependency-name: gitpython dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Add new args for mover.old * Revert "Revert "Add Aggregate Group Speed Capabilities"" This reverts commitf112b2c92e
. Fixes bug in original commit code * Remove username and password from debug logs sinced they are redacted anyways * adds support for #513 * Fixes #501 * Fixes bug not logging torrents being deleted after meeting share_limits * Adds additional error checking for invalid share_limits in config * fixes bug in torrents not untagging min_seeding_time_tag * Fixes bug in #494 * Bi-directional Wiki Sync Action * Bump actions/checkout from 2 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * Bug fix on share limits being applied every run * Revert "Revert "Revert "Add Aggregate Group Speed Capabilities""" This reverts commit41d5170ba6
. * Fixes #494 with new enable_group_upload_speed flag * Fixes bug found in #494 * 4.1.0 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Paul Walker <walkerp1@yahoo.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: bakerboy448 <55419169+bakerboy448@users.noreply.github.com>
This commit is contained in:
parent
29690e9581
commit
eccc96e388
16 changed files with 193 additions and 61 deletions
|
@ -19,7 +19,7 @@ jobs:
|
||||||
# will not occur.
|
# will not occur.
|
||||||
- name: Dependabot metadata
|
- name: Dependabot metadata
|
||||||
id: dependabot-metadata
|
id: dependabot-metadata
|
||||||
uses: dependabot/fetch-metadata@v1.6.0
|
uses: dependabot/fetch-metadata@v2.0.0
|
||||||
with:
|
with:
|
||||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
# Here the PR gets approved.
|
# Here the PR gets approved.
|
||||||
|
|
50
.github/workflows/docs.yml
vendored
Normal file
50
.github/workflows/docs.yml
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
name: Documentation
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
paths:
|
||||||
|
- "docs/**"
|
||||||
|
repository_dispatch:
|
||||||
|
types: [docs]
|
||||||
|
gollum:
|
||||||
|
|
||||||
|
env:
|
||||||
|
GIT_AUTHOR_NAME: Actionbot
|
||||||
|
GIT_AUTHOR_EMAIL: actions@github.com
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
job-sync-docs-to-wiki:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name != 'gollum'
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Sync docs to wiki
|
||||||
|
uses: newrelic/wiki-sync-action@main
|
||||||
|
with:
|
||||||
|
source: docs
|
||||||
|
destination: wiki
|
||||||
|
token: ${{ secrets.PAT }}
|
||||||
|
gitAuthorName: ${{ env.GIT_AUTHOR_NAME }}
|
||||||
|
gitAuthorEmail: ${{ env.GIT_AUTHOR_EMAIL }}
|
||||||
|
|
||||||
|
job-sync-wiki-to-docs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'gollum'
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.PAT }} # allows us to push back to repo
|
||||||
|
ref: develop
|
||||||
|
- name: Sync Wiki to Docs
|
||||||
|
uses: newrelic/wiki-sync-action@main
|
||||||
|
with:
|
||||||
|
source: wiki
|
||||||
|
destination: docs
|
||||||
|
token: ${{ secrets.PAT }}
|
||||||
|
gitAuthorName: ${{ env.GIT_AUTHOR_NAME }}
|
||||||
|
gitAuthorEmail: ${{ env.GIT_AUTHOR_EMAIL }}
|
||||||
|
branch: develop
|
2
.github/workflows/version.yml
vendored
2
.github/workflows/version.yml
vendored
|
@ -61,7 +61,7 @@ jobs:
|
||||||
ghcr.io/${{ env.OWNER_LC }}/qbit_manage:${{ steps.get_version.outputs.VERSION }}
|
ghcr.io/${{ env.OWNER_LC }}/qbit_manage:${{ steps.get_version.outputs.VERSION }}
|
||||||
- name: Create release
|
- name: Create release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
body_path: CHANGELOG
|
body_path: CHANGELOG
|
||||||
token: ${{ secrets.PAT }}
|
token: ${{ secrets.PAT }}
|
||||||
|
|
|
@ -17,7 +17,7 @@ repos:
|
||||||
- id: pretty-format-json
|
- id: pretty-format-json
|
||||||
args: [--autofix, --indent, '4', --no-sort-keys]
|
args: [--autofix, --indent, '4', --no-sort-keys]
|
||||||
- repo: https://github.com/hhatto/autopep8
|
- repo: https://github.com/hhatto/autopep8
|
||||||
rev: v2.0.4
|
rev: v2.1.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: autopep8
|
- id: autopep8
|
||||||
- repo: https://github.com/adrienverge/yamllint.git
|
- repo: https://github.com/adrienverge/yamllint.git
|
||||||
|
@ -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.15.1
|
rev: v3.15.2
|
||||||
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.2.0
|
rev: 24.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
language_version: python3
|
language_version: python3
|
||||||
|
|
25
CHANGELOG
25
CHANGELOG
|
@ -1,22 +1,17 @@
|
||||||
# Requirements Updated
|
# Requirements Updated
|
||||||
- qbittorrent-api==2024.2.59
|
- qbittorrent-api==2024.3.60
|
||||||
- GitPython==3.1.42
|
- GitPython==3.1.43
|
||||||
- ruamel.yaml==0.18.6
|
|
||||||
|
|
||||||
# New Features
|
# New Features
|
||||||
- Adds support for filtering more than just Completed torrents. Closes [#115](https://github.com/StuffAnThings/qbit_manage/issues/115)
|
- Added Group Uplaod Speed to share limits config to apply upload speed limits at a group level. Closes [#494](https://github.com/StuffAnThings/qbit_manage/issues/494)
|
||||||
- Updates mover script (Add check if file is still on cache mount #493)
|
- Added Force Retag All in config settings to force retagging of all torrents. Closes [#513](https://github.com/StuffAnThings/qbit_manage/issues/513)
|
||||||
- Adds support for ghcr.io container registry
|
- Added `--mover-old` command in mover script for users that use Mover Tuning Plugin
|
||||||
- Adds support for custom [share_limits/cross-seed tags](https://github.com/StuffAnThings/qbit_manage/commit/9f8be69a4f2680501d492a8c7148969ae5ac5b72#diff-e5794b6d2186004aa3ee69cd4dee7bbd48d8e0edd9f1da90d03393ec28cbf912) Closes [#457](https://github.com/StuffAnThings/qbit_manage/issues/457)
|
|
||||||
|
|
||||||
|
|
||||||
# Bug Fixes
|
# Bug Fixes
|
||||||
- Fixes [#359](https://github.com/StuffAnThings/qbit_manage/issues/359)
|
- Fixes [#501](https://github.com/StuffAnThings/qbit_manage/issues/501)
|
||||||
- Fixes [#479](https://github.com/StuffAnThings/qbit_manage/issues/479)
|
- Adds additional error checking for invalid share limits defined in config
|
||||||
- Fixes [#487](https://github.com/StuffAnThings/qbit_manage/issues/487)
|
- Minor bug fixes in share limits
|
||||||
- Fixes [#488](https://github.com/StuffAnThings/qbit_manage/issues/488)
|
|
||||||
- Fixes [#490](https://github.com/StuffAnThings/qbit_manage/issues/490)
|
|
||||||
- Update script header so that env python3 is used.
|
|
||||||
|
|
||||||
Special thanks to @NooNameR, @ShanaryS, @ext4xfs for their contributions!
|
Special thanks to @walkerp1 for their contributions!
|
||||||
**Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.0.8...v4.0.9
|
**Full Changelog**: https://github.com/StuffAnThings/qbit_manage/compare/v4.0.9...v4.1.0
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
4.0.9
|
4.1.0
|
||||||
|
|
|
@ -43,6 +43,7 @@ settings:
|
||||||
cat_filter_completed: True # Filters for completed torrents only when running cat_update command
|
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
|
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
|
tag_nohardlinks_filter_completed: True # Filters for completed torrents only when running tag_nohardlinks command
|
||||||
|
force_retag_all: false # Forces all torrents to be updated on every run (may cause performance issues). This does not remove any previous tags assigned
|
||||||
directory:
|
directory:
|
||||||
# Do not remove these
|
# Do not remove these
|
||||||
# Cross-seed var: </your/path/here/> # Output directory of cross-seed
|
# Cross-seed var: </your/path/here/> # Output directory of cross-seed
|
||||||
|
@ -189,6 +190,7 @@ share_limits:
|
||||||
# Will default to -1 (no limit) if not specified for the group.
|
# Will default to -1 (no limit) if not specified for the group.
|
||||||
max_seeding_time: 129600
|
max_seeding_time: 129600
|
||||||
# <OPTIONAL> min_seeding_time <int>: Will prevent torrent deletion by cleanup variable if torrent has not yet minimum seeding time (minutes).
|
# <OPTIONAL> min_seeding_time <int>: Will prevent torrent deletion by cleanup variable if torrent has not yet minimum seeding time (minutes).
|
||||||
|
# This should only be set if you are using this in conjunction with max_seeding_time and max_ratio. If you are not setting a max_ratio, then use max_seeding_time instead.
|
||||||
# If the torrent has not yet reached this minimum seeding time, it will change the share limits back to no limits and resume the torrent to continue seeding.
|
# If the torrent has not yet reached this minimum seeding time, it will change the share limits back to no limits and resume the torrent to continue seeding.
|
||||||
# Will default to 0 if not specified for the group.
|
# Will default to 0 if not specified for the group.
|
||||||
min_seeding_time: 43200
|
min_seeding_time: 43200
|
||||||
|
@ -198,6 +200,8 @@ share_limits:
|
||||||
last_active: 43200
|
last_active: 43200
|
||||||
# <OPTIONAL> Limit Upload Speed <int>: Will limit the upload speed KiB/s (KiloBytes/second) (`-1` : No Limit)
|
# <OPTIONAL> Limit Upload Speed <int>: Will limit the upload speed KiB/s (KiloBytes/second) (`-1` : No Limit)
|
||||||
limit_upload_speed: 0
|
limit_upload_speed: 0
|
||||||
|
# <OPTIONAL> Enable Group Upload Speed <bool>: Upload speed limits are applied at the group level. This will take limit_upload_speed defined and divide it equally among the number of torrents for the group.
|
||||||
|
enable_group_upload_speed: false
|
||||||
# <OPTIONAL> cleanup <bool>: WARNING!! Setting this as true Will remove and delete contents of any torrents that satisfies the share limits (max time OR max ratio)
|
# <OPTIONAL> cleanup <bool>: WARNING!! Setting this as true Will remove and delete contents of any torrents that satisfies the share limits (max time OR max ratio)
|
||||||
cleanup: false
|
cleanup: false
|
||||||
# <OPTIONAL> resume_torrent_after_change <bool>: This variable will resume your torrent after changing share limits. Default is true
|
# <OPTIONAL> resume_torrent_after_change <bool>: This variable will resume your torrent after changing share limits. Default is true
|
||||||
|
|
|
@ -187,6 +187,9 @@ class Config:
|
||||||
"tag_nohardlinks_filter_completed": self.util.check_for_attribute(
|
"tag_nohardlinks_filter_completed": self.util.check_for_attribute(
|
||||||
self.data, "tag_nohardlinks_filter_completed", parent="settings", var_type="bool", default=True
|
self.data, "tag_nohardlinks_filter_completed", parent="settings", var_type="bool", default=True
|
||||||
),
|
),
|
||||||
|
"force_retag_all": self.util.check_for_attribute(
|
||||||
|
self.data, "force_retag_all", parent="settings", var_type="bool", default=False
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tracker_error_tag = self.settings["tracker_error_tag"]
|
self.tracker_error_tag = self.settings["tracker_error_tag"]
|
||||||
|
@ -465,6 +468,16 @@ class Config:
|
||||||
do_print=False,
|
do_print=False,
|
||||||
save=False,
|
save=False,
|
||||||
)
|
)
|
||||||
|
self.share_limits[group]["enable_group_upload_speed"] = self.util.check_for_attribute(
|
||||||
|
self.data,
|
||||||
|
"enable_group_upload_speed",
|
||||||
|
parent="share_limits",
|
||||||
|
subparent=group,
|
||||||
|
var_type="bool",
|
||||||
|
default=False,
|
||||||
|
do_print=False,
|
||||||
|
save=False,
|
||||||
|
)
|
||||||
self.share_limits[group]["min_num_seeds"] = self.util.check_for_attribute(
|
self.share_limits[group]["min_num_seeds"] = self.util.check_for_attribute(
|
||||||
self.data,
|
self.data,
|
||||||
"min_num_seeds",
|
"min_num_seeds",
|
||||||
|
@ -508,6 +521,25 @@ class Config:
|
||||||
save=False,
|
save=False,
|
||||||
)
|
)
|
||||||
self.share_limits[group]["torrents"] = []
|
self.share_limits[group]["torrents"] = []
|
||||||
|
if (
|
||||||
|
self.share_limits[group]["min_seeding_time"] > 0
|
||||||
|
and self.share_limits[group]["min_seeding_time"] > self.share_limits[group]["max_seeding_time"]
|
||||||
|
):
|
||||||
|
err = (
|
||||||
|
f"Config Error: min_seeding_time ({self.share_limits[group]['min_seeding_time']}) is greater than "
|
||||||
|
f"max_seeding_time ({self.share_limits[group]['max_seeding_time']}) for the grouping '{group}'.\n"
|
||||||
|
f"min_seeding_time must be less than or equal to max_seeding_time."
|
||||||
|
)
|
||||||
|
self.notify(err, "Config")
|
||||||
|
raise Failed(err)
|
||||||
|
if self.share_limits[group]["min_seeding_time"] > 0 and self.share_limits[group]["max_ratio"] <= 0:
|
||||||
|
err = (
|
||||||
|
f"Config Error: min_seeding_time ({self.share_limits[group]['min_seeding_time']}) is set, "
|
||||||
|
f"but max_ratio ({self.share_limits[group]['max_ratio']}) is not set for the grouping '{group}'.\n"
|
||||||
|
f"max_ratio must be greater than 0 when min_seeding_time is set."
|
||||||
|
)
|
||||||
|
self.notify(err, "Config")
|
||||||
|
raise Failed(err)
|
||||||
else:
|
else:
|
||||||
if self.commands["share_limits"]:
|
if self.commands["share_limits"]:
|
||||||
err = "Config Error: share_limits. No valid grouping found."
|
err = "Config Error: share_limits. No valid grouping found."
|
||||||
|
@ -721,7 +753,7 @@ class Config:
|
||||||
if not self.dry_run:
|
if not self.dry_run:
|
||||||
for path in location_path_list:
|
for path in location_path_list:
|
||||||
if path != location_path:
|
if path != location_path:
|
||||||
util.remove_empty_directories(path, "**/*")
|
util.remove_empty_directories(path, "**/*", self.qbt.get_category_save_paths())
|
||||||
body += logger.print_line(
|
body += logger.print_line(
|
||||||
f"{'Did not delete' if self.dry_run else 'Deleted'} {num_del} files "
|
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}.",
|
||||||
|
|
|
@ -85,7 +85,10 @@ class RemoveOrphaned:
|
||||||
if not self.config.dry_run:
|
if not self.config.dry_run:
|
||||||
orphaned_parent_path = set(self.executor.map(self.move_orphan, orphaned_files))
|
orphaned_parent_path = set(self.executor.map(self.move_orphan, 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(lambda dir: util.remove_empty_directories(dir, "**/*"), orphaned_parent_path)
|
self.executor.map(
|
||||||
|
lambda dir: util.remove_empty_directories(dir, "**/*", self.qbt.get_category_save_paths()),
|
||||||
|
orphaned_parent_path,
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.print_line("No Orphaned Files found.", self.config.loglevel)
|
logger.print_line("No Orphaned Files found.", self.config.loglevel)
|
||||||
|
|
|
@ -170,6 +170,8 @@ class ShareLimits:
|
||||||
logger.separator(
|
logger.separator(
|
||||||
f"Updating Share Limits for [Group {group_name}] [Priority {group_config['priority']}]", space=False, border=False
|
f"Updating Share Limits for [Group {group_name}] [Priority {group_config['priority']}]", space=False, border=False
|
||||||
)
|
)
|
||||||
|
group_upload_speed = group_config["limit_upload_speed"]
|
||||||
|
|
||||||
for torrent in torrents:
|
for torrent in torrents:
|
||||||
t_name = torrent.name
|
t_name = torrent.name
|
||||||
t_hash = torrent.hash
|
t_hash = torrent.hash
|
||||||
|
@ -181,8 +183,16 @@ class ShareLimits:
|
||||||
check_max_seeding_time = group_config["max_seeding_time"] != torrent.max_seeding_time
|
check_max_seeding_time = group_config["max_seeding_time"] != torrent.max_seeding_time
|
||||||
# Treat upload limit as -1 if it is set to 0 (unlimited)
|
# Treat upload limit as -1 if it is set to 0 (unlimited)
|
||||||
torrent_upload_limit = -1 if round(torrent.up_limit / 1024) == 0 else round(torrent.up_limit / 1024)
|
torrent_upload_limit = -1 if round(torrent.up_limit / 1024) == 0 else round(torrent.up_limit / 1024)
|
||||||
if group_config["limit_upload_speed"] == 0:
|
if group_config["limit_upload_speed"] <= 0:
|
||||||
group_config["limit_upload_speed"] = -1
|
group_config["limit_upload_speed"] = -1
|
||||||
|
else:
|
||||||
|
if group_config["enable_group_upload_speed"]:
|
||||||
|
logger.trace(
|
||||||
|
"enable_group_upload_speed set to True.\n"
|
||||||
|
f"Setting limit_upload_speed to {group_upload_speed} / {len(torrents)} = "
|
||||||
|
f"{round(group_upload_speed / len(torrents))} kB/s"
|
||||||
|
)
|
||||||
|
group_config["limit_upload_speed"] = round(group_upload_speed / len(torrents))
|
||||||
check_limit_upload_speed = group_config["limit_upload_speed"] != torrent_upload_limit
|
check_limit_upload_speed = group_config["limit_upload_speed"] != torrent_upload_limit
|
||||||
hash_not_prev_checked = t_hash not in self.torrent_hash_checked
|
hash_not_prev_checked = t_hash not in self.torrent_hash_checked
|
||||||
share_limits_not_yet_tagged = (
|
share_limits_not_yet_tagged = (
|
||||||
|
@ -207,21 +217,6 @@ class ShareLimits:
|
||||||
logger.trace(f"check_limit_upload_speed: {check_limit_upload_speed}")
|
logger.trace(f"check_limit_upload_speed: {check_limit_upload_speed}")
|
||||||
logger.trace(f"hash_not_prev_checked: {hash_not_prev_checked}")
|
logger.trace(f"hash_not_prev_checked: {hash_not_prev_checked}")
|
||||||
logger.trace(f"share_limits_not_yet_tagged: {share_limits_not_yet_tagged}")
|
logger.trace(f"share_limits_not_yet_tagged: {share_limits_not_yet_tagged}")
|
||||||
if (
|
|
||||||
check_max_ratio or check_max_seeding_time or check_limit_upload_speed or share_limits_not_yet_tagged
|
|
||||||
) and hash_not_prev_checked:
|
|
||||||
if (
|
|
||||||
not is_tag_in_torrent(self.min_seeding_time_tag, torrent.tags)
|
|
||||||
and not is_tag_in_torrent(self.min_num_seeds_tag, torrent.tags)
|
|
||||||
and not is_tag_in_torrent(self.last_active_tag, torrent.tags)
|
|
||||||
):
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
self.stats_tagged += 1
|
|
||||||
self.torrents_updated.append(t_name)
|
|
||||||
|
|
||||||
tor_reached_seed_limit = self.has_reached_seed_limit(
|
tor_reached_seed_limit = self.has_reached_seed_limit(
|
||||||
torrent=torrent,
|
torrent=torrent,
|
||||||
|
@ -233,6 +228,24 @@ class ShareLimits:
|
||||||
resume_torrent=group_config["resume_torrent_after_change"],
|
resume_torrent=group_config["resume_torrent_after_change"],
|
||||||
tracker=tracker["url"],
|
tracker=tracker["url"],
|
||||||
)
|
)
|
||||||
|
# Get updated torrent after checking if the torrent has reached seed limits
|
||||||
|
torrent = self.qbt.get_torrents({"torrent_hashes": t_hash})[0]
|
||||||
|
if (
|
||||||
|
check_max_ratio or check_max_seeding_time or check_limit_upload_speed or share_limits_not_yet_tagged
|
||||||
|
) and hash_not_prev_checked:
|
||||||
|
if (
|
||||||
|
not is_tag_in_torrent(self.min_seeding_time_tag, torrent.tags)
|
||||||
|
and not is_tag_in_torrent(self.min_num_seeds_tag, torrent.tags)
|
||||||
|
and not is_tag_in_torrent(self.last_active_tag, torrent.tags)
|
||||||
|
) or share_limits_not_yet_tagged:
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
self.stats_tagged += 1
|
||||||
|
self.torrents_updated.append(t_name)
|
||||||
|
|
||||||
# Cleanup torrents if the torrent meets the criteria for deletion and cleanup is enabled
|
# Cleanup torrents if the torrent meets the criteria for deletion and cleanup is enabled
|
||||||
if group_config["cleanup"]:
|
if group_config["cleanup"]:
|
||||||
if tor_reached_seed_limit:
|
if tor_reached_seed_limit:
|
||||||
|
@ -359,8 +372,7 @@ class ShareLimits:
|
||||||
body.append(msg)
|
body.append(msg)
|
||||||
# Update Torrents
|
# Update Torrents
|
||||||
if not self.config.dry_run:
|
if not self.config.dry_run:
|
||||||
if tags and not is_tag_in_torrent(tags, torrent.tags):
|
torrent.add_tags(tags)
|
||||||
torrent.add_tags(tags)
|
|
||||||
torrent_upload_limit = -1 if round(torrent.up_limit / 1024) == 0 else round(torrent.up_limit / 1024)
|
torrent_upload_limit = -1 if round(torrent.up_limit / 1024) == 0 else round(torrent.up_limit / 1024)
|
||||||
if limit_upload_speed is not None and limit_upload_speed != torrent_upload_limit:
|
if limit_upload_speed is not None and limit_upload_speed != torrent_upload_limit:
|
||||||
if limit_upload_speed == -1:
|
if limit_upload_speed == -1:
|
||||||
|
@ -385,16 +397,22 @@ class ShareLimits:
|
||||||
):
|
):
|
||||||
"""Check if torrent has reached seed limit"""
|
"""Check if torrent has reached seed limit"""
|
||||||
body = ""
|
body = ""
|
||||||
|
torrent_tags = torrent.tags
|
||||||
|
|
||||||
|
def _remove_min_seeding_time_tag():
|
||||||
|
nonlocal torrent_tags
|
||||||
|
if is_tag_in_torrent(self.min_seeding_time_tag, torrent_tags):
|
||||||
|
if not self.config.dry_run:
|
||||||
|
torrent.remove_tags(tags=self.min_seeding_time_tag)
|
||||||
|
|
||||||
def _has_reached_min_seeding_time_limit():
|
def _has_reached_min_seeding_time_limit():
|
||||||
|
nonlocal torrent_tags
|
||||||
print_log = []
|
print_log = []
|
||||||
if torrent.seeding_time >= min_seeding_time * 60:
|
if torrent.seeding_time >= min_seeding_time * 60:
|
||||||
if is_tag_in_torrent(self.min_seeding_time_tag, torrent.tags):
|
_remove_min_seeding_time_tag()
|
||||||
if not self.config.dry_run:
|
|
||||||
torrent.remove_tags(tags=self.min_seeding_time_tag)
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if not is_tag_in_torrent(self.min_seeding_time_tag, torrent.tags):
|
if not is_tag_in_torrent(self.min_seeding_time_tag, torrent_tags):
|
||||||
print_log += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
|
print_log += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
|
||||||
print_log += logger.print_line(logger.insert_space(f"Tracker: {tracker}", 8), self.config.loglevel)
|
print_log += logger.print_line(logger.insert_space(f"Tracker: {tracker}", 8), self.config.loglevel)
|
||||||
print_log += logger.print_line(
|
print_log += logger.print_line(
|
||||||
|
@ -411,20 +429,22 @@ class ShareLimits:
|
||||||
)
|
)
|
||||||
if not self.config.dry_run:
|
if not self.config.dry_run:
|
||||||
torrent.add_tags(self.min_seeding_time_tag)
|
torrent.add_tags(self.min_seeding_time_tag)
|
||||||
|
torrent_tags += f", {self.min_seeding_time_tag}"
|
||||||
torrent.set_share_limits(ratio_limit=-1, seeding_time_limit=-1, inactive_seeding_time_limit=-1)
|
torrent.set_share_limits(ratio_limit=-1, seeding_time_limit=-1, inactive_seeding_time_limit=-1)
|
||||||
if resume_torrent:
|
if resume_torrent:
|
||||||
torrent.resume()
|
torrent.resume()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _is_less_than_min_num_seeds():
|
def _is_less_than_min_num_seeds():
|
||||||
|
nonlocal torrent_tags
|
||||||
print_log = []
|
print_log = []
|
||||||
if min_num_seeds == 0 or torrent.num_complete >= min_num_seeds:
|
if min_num_seeds == 0 or torrent.num_complete >= min_num_seeds:
|
||||||
if is_tag_in_torrent(self.min_num_seeds_tag, torrent.tags):
|
if is_tag_in_torrent(self.min_num_seeds_tag, torrent_tags):
|
||||||
if not self.config.dry_run:
|
if not self.config.dry_run:
|
||||||
torrent.remove_tags(tags=self.min_num_seeds_tag)
|
torrent.remove_tags(tags=self.min_num_seeds_tag)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if not is_tag_in_torrent(self.min_num_seeds_tag, torrent.tags):
|
if not is_tag_in_torrent(self.min_num_seeds_tag, torrent_tags):
|
||||||
print_log += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
|
print_log += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
|
||||||
print_log += logger.print_line(logger.insert_space(f"Tracker: {tracker}", 8), self.config.loglevel)
|
print_log += logger.print_line(logger.insert_space(f"Tracker: {tracker}", 8), self.config.loglevel)
|
||||||
print_log += logger.print_line(
|
print_log += logger.print_line(
|
||||||
|
@ -441,22 +461,24 @@ class ShareLimits:
|
||||||
)
|
)
|
||||||
if not self.config.dry_run:
|
if not self.config.dry_run:
|
||||||
torrent.add_tags(self.min_num_seeds_tag)
|
torrent.add_tags(self.min_num_seeds_tag)
|
||||||
|
torrent_tags += f", {self.min_num_seeds_tag}"
|
||||||
torrent.set_share_limits(ratio_limit=-1, seeding_time_limit=-1, inactive_seeding_time_limit=-1)
|
torrent.set_share_limits(ratio_limit=-1, seeding_time_limit=-1, inactive_seeding_time_limit=-1)
|
||||||
if resume_torrent:
|
if resume_torrent:
|
||||||
torrent.resume()
|
torrent.resume()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _has_reached_last_active_time_limit():
|
def _has_reached_last_active_time_limit():
|
||||||
|
nonlocal torrent_tags
|
||||||
print_log = []
|
print_log = []
|
||||||
now = int(time())
|
now = int(time())
|
||||||
inactive_time_minutes = round((now - torrent.last_activity) / 60)
|
inactive_time_minutes = round((now - torrent.last_activity) / 60)
|
||||||
if inactive_time_minutes >= last_active:
|
if inactive_time_minutes >= last_active:
|
||||||
if is_tag_in_torrent(self.last_active_tag, torrent.tags):
|
if is_tag_in_torrent(self.last_active_tag, torrent_tags):
|
||||||
if not self.config.dry_run:
|
if not self.config.dry_run:
|
||||||
torrent.remove_tags(tags=self.last_active_tag)
|
torrent.remove_tags(tags=self.last_active_tag)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if not is_tag_in_torrent(self.last_active_tag, torrent.tags):
|
if not is_tag_in_torrent(self.last_active_tag, torrent_tags):
|
||||||
print_log += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
|
print_log += logger.print_line(logger.insert_space(f"Torrent Name: {torrent.name}", 3), self.config.loglevel)
|
||||||
print_log += logger.print_line(logger.insert_space(f"Tracker: {tracker}", 8), self.config.loglevel)
|
print_log += logger.print_line(logger.insert_space(f"Tracker: {tracker}", 8), self.config.loglevel)
|
||||||
print_log += logger.print_line(
|
print_log += logger.print_line(
|
||||||
|
@ -473,6 +495,7 @@ class ShareLimits:
|
||||||
)
|
)
|
||||||
if not self.config.dry_run:
|
if not self.config.dry_run:
|
||||||
torrent.add_tags(self.last_active_tag)
|
torrent.add_tags(self.last_active_tag)
|
||||||
|
torrent_tags += f", {self.last_active_tag}"
|
||||||
torrent.set_share_limits(ratio_limit=-1, seeding_time_limit=-1, inactive_seeding_time_limit=-1)
|
torrent.set_share_limits(ratio_limit=-1, seeding_time_limit=-1, inactive_seeding_time_limit=-1)
|
||||||
if resume_torrent:
|
if resume_torrent:
|
||||||
torrent.resume()
|
torrent.resume()
|
||||||
|
@ -488,9 +511,10 @@ class ShareLimits:
|
||||||
elif max_seeding_time == -2 and self.qbt.global_max_seeding_time_enabled:
|
elif max_seeding_time == -2 and self.qbt.global_max_seeding_time_enabled:
|
||||||
seeding_time_limit = self.qbt.global_max_seeding_time
|
seeding_time_limit = self.qbt.global_max_seeding_time
|
||||||
else:
|
else:
|
||||||
|
_remove_min_seeding_time_tag()
|
||||||
return False
|
return False
|
||||||
if seeding_time_limit:
|
if seeding_time_limit:
|
||||||
if (torrent.seeding_time >= seeding_time_limit * 60) and _has_reached_min_seeding_time_limit():
|
if _has_reached_min_seeding_time_limit() and (torrent.seeding_time >= seeding_time_limit * 60):
|
||||||
body += logger.insert_space(
|
body += logger.insert_space(
|
||||||
f"Seeding Time vs Max Seed Time: {timedelta(seconds=torrent.seeding_time)} >= "
|
f"Seeding Time vs Max Seed Time: {timedelta(seconds=torrent.seeding_time)} >= "
|
||||||
f"{timedelta(minutes=seeding_time_limit)}",
|
f"{timedelta(minutes=seeding_time_limit)}",
|
||||||
|
|
|
@ -13,6 +13,7 @@ class Tags:
|
||||||
self.default_ignore_tags = qbit_manager.config.default_ignore_tags # default ignore tags
|
self.default_ignore_tags = qbit_manager.config.default_ignore_tags # default ignore tags
|
||||||
self.torrents_updated = [] # List of torrents updated
|
self.torrents_updated = [] # List of torrents updated
|
||||||
self.notify_attr = [] # List of single torrent attributes to send to notifiarr
|
self.notify_attr = [] # List of single torrent attributes to send to notifiarr
|
||||||
|
self.force_retag = qbit_manager.config.settings["force_retag_all"] # Force retag of all torrents
|
||||||
|
|
||||||
self.tags()
|
self.tags()
|
||||||
self.config.webhooks_factory.notify(self.torrents_updated, self.notify_attr, group_by="tag")
|
self.config.webhooks_factory.notify(self.torrents_updated, self.notify_attr, group_by="tag")
|
||||||
|
@ -25,7 +26,7 @@ class Tags:
|
||||||
for torrent in self.qbt.torrent_list:
|
for torrent in self.qbt.torrent_list:
|
||||||
check_tags = [tag for tag in util.get_list(torrent.tags) if self.share_limits_tag not in tag]
|
check_tags = [tag for tag in util.get_list(torrent.tags) if self.share_limits_tag not in tag]
|
||||||
|
|
||||||
if torrent.tags == "" or (len([trk for trk in check_tags if trk not in ignore_tags]) == 0):
|
if torrent.tags == "" or (len([trk for trk in check_tags if trk not in ignore_tags]) == 0) or self.force_retag:
|
||||||
tracker = self.qbt.get_tags(torrent.trackers)
|
tracker = self.qbt.get_tags(torrent.trackers)
|
||||||
if tracker["tag"]:
|
if tracker["tag"]:
|
||||||
t_name = torrent.name
|
t_name = torrent.name
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from functools import cache
|
||||||
|
|
||||||
from qbittorrentapi import Client
|
from qbittorrentapi import Client
|
||||||
from qbittorrentapi import LoginFailed
|
from qbittorrentapi import LoginFailed
|
||||||
|
@ -33,7 +34,7 @@ class Qbt:
|
||||||
self.password = params["password"]
|
self.password = params["password"]
|
||||||
logger.secret(self.username)
|
logger.secret(self.username)
|
||||||
logger.secret(self.password)
|
logger.secret(self.password)
|
||||||
logger.debug(f"Host: {self.host}, Username: {self.username}, Password: {self.password}")
|
logger.debug(f"Host: {self.host}")
|
||||||
ex = ""
|
ex = ""
|
||||||
try:
|
try:
|
||||||
self.client = Client(
|
self.client = Client(
|
||||||
|
@ -357,6 +358,7 @@ class Qbt:
|
||||||
logger.warning(e)
|
logger.warning(e)
|
||||||
return tracker
|
return tracker
|
||||||
|
|
||||||
|
@cache
|
||||||
def get_category(self, path):
|
def get_category(self, path):
|
||||||
"""Get category from config file based on path provided"""
|
"""Get category from config file based on path provided"""
|
||||||
category = ""
|
category = ""
|
||||||
|
@ -378,6 +380,17 @@ class Qbt:
|
||||||
logger.warning(e)
|
logger.warning(e)
|
||||||
return category
|
return category
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def get_category_save_paths(self):
|
||||||
|
"""Get all categories from qbitorrenta and return a list of save_paths"""
|
||||||
|
save_paths = set()
|
||||||
|
categories = self.client.torrent_categories.categories
|
||||||
|
for cat in categories:
|
||||||
|
save_path = categories[cat].savePath.replace(self.config.root_dir, self.config.remote_dir)
|
||||||
|
if save_path:
|
||||||
|
save_paths.add(save_path)
|
||||||
|
return list(save_paths)
|
||||||
|
|
||||||
def tor_delete_recycle(self, torrent, info):
|
def tor_delete_recycle(self, torrent, info):
|
||||||
"""Move torrent to recycle bin"""
|
"""Move torrent to recycle bin"""
|
||||||
try:
|
try:
|
||||||
|
@ -477,7 +490,7 @@ class Qbt:
|
||||||
# Delete torrent and files
|
# Delete torrent and files
|
||||||
torrent.delete(delete_files=to_delete)
|
torrent.delete(delete_files=to_delete)
|
||||||
# Remove any empty directories
|
# Remove any empty directories
|
||||||
util.remove_empty_directories(save_path, "**/*")
|
util.remove_empty_directories(save_path, "**/*", self.get_category_save_paths())
|
||||||
else:
|
else:
|
||||||
torrent.delete(delete_files=False)
|
torrent.delete(delete_files=False)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -421,7 +421,7 @@ def copy_files(src, dest):
|
||||||
logger.error(ex)
|
logger.error(ex)
|
||||||
|
|
||||||
|
|
||||||
def remove_empty_directories(pathlib_root_dir, pattern):
|
def remove_empty_directories(pathlib_root_dir, pattern, excluded_paths=None):
|
||||||
"""Remove empty directories recursively."""
|
"""Remove empty directories recursively."""
|
||||||
pathlib_root_dir = Path(pathlib_root_dir)
|
pathlib_root_dir = Path(pathlib_root_dir)
|
||||||
try:
|
try:
|
||||||
|
@ -435,6 +435,8 @@ def remove_empty_directories(pathlib_root_dir, pattern):
|
||||||
longest.append(pathlib_root_dir) # delete the folder itself if it's empty
|
longest.append(pathlib_root_dir) # delete the folder itself if it's empty
|
||||||
for pdir in longest:
|
for pdir in longest:
|
||||||
try:
|
try:
|
||||||
|
if str(pdir) in excluded_paths:
|
||||||
|
continue
|
||||||
pdir.rmdir() # remove directory if empty
|
pdir.rmdir() # remove directory if empty
|
||||||
except (FileNotFoundError, OSError):
|
except (FileNotFoundError, OSError):
|
||||||
continue # catch and continue if non-empty, folders within could already be deleted if run in parallel
|
continue # catch and continue if non-empty, folders within could already be deleted if run in parallel
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
flake8==7.0.0
|
flake8==7.0.0
|
||||||
pre-commit==3.6.2
|
pre-commit==3.7.0
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
bencodepy==0.9.5
|
bencodepy==0.9.5
|
||||||
GitPython==3.1.42
|
GitPython==3.1.43
|
||||||
qbittorrent-api==2024.2.59
|
qbittorrent-api==2024.3.60
|
||||||
requests==2.31.0
|
requests==2.31.0
|
||||||
retrying==1.3.4
|
retrying==1.3.4
|
||||||
ruamel.yaml==0.18.6
|
ruamel.yaml==0.18.6
|
||||||
|
|
|
@ -16,13 +16,19 @@ parser.add_argument(
|
||||||
"--cache-mount",
|
"--cache-mount",
|
||||||
"--cache_mount",
|
"--cache_mount",
|
||||||
help="Cache mount point in Unraid. This is used to additionally filter for only torrents that exists on the cache mount."
|
help="Cache mount point in Unraid. This is used to additionally filter for only torrents that exists on the cache mount."
|
||||||
"Use this option ONLY if you follow TRaSH Guides folder structure.",
|
"Use this option ONLY if you follow TRaSH Guides folder structure. (For default cache drive set this to /mnt/cache)",
|
||||||
default=None,
|
default=None,
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--days-from", "--days_from", help="Set Number of Days to stop torrents between two offsets", type=int, default=0
|
"--days-from", "--days_from", help="Set Number of Days to stop torrents between two offsets", type=int, default=0
|
||||||
)
|
)
|
||||||
parser.add_argument("--days-to", "--days_to", help="Set Number of Days to stop torrents between two offsets", type=int, default=2)
|
parser.add_argument("--days-to", "--days_to", help="Set Number of Days to stop torrents between two offsets", type=int, default=2)
|
||||||
|
parser.add_argument(
|
||||||
|
"--mover-old",
|
||||||
|
help="Use mover.old instead of mover. Useful if you're using the Mover Tuning Plugin",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
# --DEFINE VARIABLES--#
|
# --DEFINE VARIABLES--#
|
||||||
|
|
||||||
# --START SCRIPT--#
|
# --START SCRIPT--#
|
||||||
|
@ -88,10 +94,12 @@ if __name__ == "__main__":
|
||||||
stop_start_torrents(torrents, True)
|
stop_start_torrents(torrents, True)
|
||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
# Start mover
|
# Start mover
|
||||||
print("Starting Mover")
|
print(f"Starting {'mover.old' if args.mover_old else 'mover'} to move files older than {args.days_to} days to array disks.")
|
||||||
# Or using mover tunning
|
# Or using mover tunning
|
||||||
# os.system('/usr/local/sbin/mover start')
|
if args.mover_old:
|
||||||
os.system("/usr/local/sbin/mover.old start")
|
os.system("/usr/local/sbin/mover.old start")
|
||||||
|
else:
|
||||||
|
os.system("/usr/local/sbin/mover start")
|
||||||
# Start Torrents
|
# Start Torrents
|
||||||
print(f"Resuming [{len(torrents)}] paused torrents from {args.days_from} - {args.days_to} days ago")
|
print(f"Resuming [{len(torrents)}] paused torrents from {args.days_from} - {args.days_to} days ago")
|
||||||
stop_start_torrents(torrents, False)
|
stop_start_torrents(torrents, False)
|
||||||
|
|
Loading…
Add table
Reference in a new issue