Skip to content

Commit

Permalink
add torrents-info, torrents-stop, and torrents-stop-incomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
chapmanjacobd committed Dec 7, 2024
1 parent a362a06 commit 6e04b77
Show file tree
Hide file tree
Showing 18 changed files with 511 additions and 58 deletions.
1 change: 1 addition & 0 deletions tests/createdb/test_computers_add.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#
1 change: 1 addition & 0 deletions tests/createdb/test_torrents_add.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#
Empty file.
1 change: 1 addition & 0 deletions tests/mediafiles/test_torrents_start.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#
1 change: 1 addition & 0 deletions tests/mediafiles/test_torrents_stop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#
1 change: 1 addition & 0 deletions tests/mediafiles/test_torrents_stop_incomplete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#
1 change: 1 addition & 0 deletions tests/multidb/test_allocate_torrents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#
1 change: 1 addition & 0 deletions tests/playback/test_torrents_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#
30 changes: 26 additions & 4 deletions xklb/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@
"images_to_pdf": "Convert folders of images into image PDFs",
"pdf_edit": "Apply brightness, contrast, saturation, and sharpness adjustments to PDFs",
"torrents_start": "Start torrents (qBittorrent-nox)",
"torrents_stop": "Stop torrents (qBittorrent-nox)",
"torrents_stop": "Stop seeding torrents (qBittorrent-nox)",
"torrents_stop_incomplete": "Stop downloading torrents (qBittorrent-nox)",
},
"Multi-database subcommands": {
"merge_dbs": "Merge SQLite databases",
Expand Down Expand Up @@ -108,6 +109,7 @@
"tabs_open": "Open your tabs for the day",
"links_open": "Open links from link dbs",
"surf": "Auto-load browser tabs in a streaming way (stdin)",
"torrents_info": "List torrents (qBittorrent-nox)",
},
"Database enrichment subcommands": {
"dedupe_db": "Dedupe SQLite tables",
Expand Down Expand Up @@ -227,6 +229,7 @@ def print_help(parser) -> None:
"xklb.mediafiles.pdf_edit.pdf_edit": [],
"xklb.mediafiles.torrents_start.torrents_start": ["torrent-start"],
"xklb.mediafiles.torrents_stop.torrents_stop": ["torrent-stop"],
"xklb.mediafiles.torrents_stop_incomplete.torrents_stop_incomplete": ["torrent-stop-incomplete"],
"xklb.misc.dedupe_czkawka.czkawka_dedupe": ["dedupe-czkawka"],
"xklb.misc.export_text.export_text": [],
"xklb.multidb.copy_play_counts.copy_play_counts": [],
Expand All @@ -242,10 +245,11 @@ def print_help(parser) -> None:
"xklb.playback.playback_control.playback_next": ["next"],
"xklb.playback.playback_control.playback_now": ["now"],
"xklb.playback.playback_control.playback_pause": ["pause", "play"],
"xklb.playback.playback_control.playback_stop": ["stop"],
"xklb.playback.playback_control.playback_seek": ["ffwd", "rewind", "seek"], # TODO: make rewind negative...
"xklb.playback.playback_control.playback_stop": ["stop"],
"xklb.playback.surf.streaming_tab_loader": ["surf"],
"xklb.playback.tabs_open.tabs_open": ["tb", "tabs", "open_tabs"],
"xklb.playback.torrents_info.torrents_info": ["torrent-info", "torrents", "torrent"],
"xklb.tablefiles.eda.eda": ["preview"],
"xklb.tablefiles.incremental_diff.incremental_diff": [],
"xklb.tablefiles.columns.columns": [],
Expand Down Expand Up @@ -277,7 +281,20 @@ def create_subcommands_parser() -> argparse.ArgumentParser:
subparsers = parser.add_subparsers()

# this needs to stay inside the function to prevent side-effects during testing
known_subcommands = ["fs", "media", "open", "table", "tables", "tabs", "du", "search", "links", "images"]
known_subcommands = [
"fs",
"media",
"open",
"table",
"tables",
"tabs",
"du",
"search",
"links",
"images",
"torrents",
"torrent",
]

def consecutive_prefixes(s):
prefixes = [s[:j] for j in range(5, len(s)) if s[:j] and s[:j] not in known_subcommands]
Expand Down Expand Up @@ -325,7 +342,12 @@ def add_parser(subparsers, func, aliases=None):

def library(args=None) -> None:
if args:
sys.argv = ["lb", *args]
original_argv = sys.argv
try:
sys.argv = ["lb", *args]
return library()
finally:
sys.argv = original_argv

parser.exit_on_error = False # type: ignore
try:
Expand Down
5 changes: 2 additions & 3 deletions xklb/createdb/torrents_add.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import concurrent.futures, os, statistics
from pathlib import Path
from urllib.parse import urlparse

from torrentool.api import Torrent

from xklb import usage
from xklb.mediadb import db_playlists
from xklb.utils import arg_utils, arggroups, argparse_utils, consts, db_utils, iterables, nums, objects, printing
from xklb.utils.log_utils import log
from xklb.utils.path_utils import domain_from_url


def parse_args():
Expand All @@ -29,8 +29,7 @@ def get_tracker(torrent: Torrent):
log.debug(torrent.announce_urls)

for tracker in iterables.flatten(torrent.announce_urls):
url = urlparse(tracker)
domain = ".".join(url.netloc.rsplit(":")[0].rsplit(".", 2)[-2:]).lower()
domain = domain_from_url(tracker)
return domain

return torrent.source
Expand Down
3 changes: 2 additions & 1 deletion xklb/mediafiles/torrents_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
import qbittorrentapi
from torrentool.api import Bencode, Torrent

from xklb import usage
from xklb.createdb.torrents_add import get_tracker
from xklb.utils import arggroups, argparse_utils, processes
from xklb.utils.file_utils import trash
from xklb.utils.log_utils import log


def parse_args():
parser = argparse_utils.ArgumentParser()
parser = argparse_utils.ArgumentParser(usage=usage.torrents_start)
arggroups.qBittorrent(parser)
arggroups.capability_delete(parser)
arggroups.debug(parser)
Expand Down
134 changes: 87 additions & 47 deletions xklb/mediafiles/torrents_stop.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,105 @@
import argparse, os
from datetime import datetime
import argparse, os, shutil
from pathlib import Path

from xklb import usage
from xklb.mediafiles.torrents_start import start_qBittorrent
from xklb.utils import arggroups
from xklb.utils import arggroups, consts, devices, iterables, nums, path_utils, printing
from xklb.utils.log_utils import log

# TODO: to stop and add the delete tag to torrents
# qbt_client.torrents_info()

# default exclude:
def parse_args():
parser = argparse.ArgumentParser(usage=usage.torrents_stop)
arggroups.qBittorrent(parser)
arggroups.capability_soft_delete(parser)
arggroups.capability_delete(parser)
arggroups.debug(parser)

# 3 or less seeders
# less than 180 days
# inactive seeding less than 30 days
# size less than 5MiB
parser.add_argument("--min-size", type=nums.human_to_bytes, default="5MiB", help="Minimum download size")
parser.add_argument("--min-days-stalled-seed", type=int, default=45, help="Minimum days since last activity")
parser.add_argument("--min-seeders", type=int, default=5, help="Minimum current seeders")
parser.add_argument("--min-days-seeding", type=int, default=180, help="Minimum days seed time")

# move files to to_process dir and start shrink
parser.add_argument("--move", help="Directory to move folders/files")

args = parser.parse_args()
return args


def filter_seeding(args, torrents):
filtered_torrents = {}
for t in torrents:
if t.total_size <= args.min_size:
status = "small_size"
elif (consts.now() - t.last_activity) <= (86400 * args.min_days_stalled_seed):
status = "recent_activity"
elif t.num_complete <= args.min_seeders:
status = "few_seeders"
elif t.time_active <= (86400 * args.min_days_seeding):
status = "insufficient_seed_time"
else:
status = "ready"
filtered_torrents.setdefault(status, []).append(t)
return filtered_torrents

def torrents_stop():
parser = argparse.ArgumentParser()
arggroups.qBittorrent(parser)

parser.add_argument("--process-prefix", default="/path/to/to_process", help="Directory to move processed files")
parser.add_argument("--min_seeders", type=int, default=3, help="Minimum number of seeders")
parser.add_argument("--min_age_days", type=int, default=180, help="Minimum age in days")
parser.add_argument("--min_active_seeding_days", type=int, default=30, help="Minimum active seeding time in days")
parser.add_argument(
"--min_size_bytes", type=int, default=5 * 1024 * 1024, help="Minimum size in bytes (5 MiB by default)"
)
args = parser.parse_args()
def torrents_stop():
args = parse_args()

qbt_client = start_qBittorrent(args)

torrents = qbt_client.torrents_info()
torrents = sorted(torrents, key=lambda t: -t.added_on)

states = ["queuedUP", "forcedUP", "stalledUP", "uploading"]
seeding = [t for t in torrents if t.state in states]

seeding_results = filter_seeding(args, seeding)
printing.table(
[
{
"seeding_status": status.replace("_", " ").title(),
"count": len(torrents),
}
for status, torrents in seeding_results.items()
]
)
print()

for torrent in torrents:
if torrent.num_complete <= args.min_seeders:
continue

added_on = datetime.fromtimestamp(torrent.added_on)
if (datetime.now() - added_on).days < args.min_age_days:
continue

if torrent.seeding_time < args.min_active_seeding_days * 24 * 60 * 60: # Convert days to seconds
continue

if torrent.size < args.min_size_bytes:
continue
torrents = seeding_results.get("ready")
if not torrents:
return
torrent_hashes = [t.hash for t in torrents]

qbt_client.torrents_pause(torrent_hashes=torrent.hash)
qbt_client.torrents_add_tags(tags="processing", torrent_hashes=torrent.hash)
if not (args.no_confirm or devices.confirm("Continue?")):
return

# Move files to to_process directory
save_path = torrent.save_path
torrent_name = torrent.name
new_path = os.path.join(args.to_process_dir, torrent_name)
qbt_client.torrents_stop(torrent_hashes=torrent_hashes)

if os.path.exists(save_path):
os.makedirs(args.to_process_dir, exist_ok=True)
os.rename(save_path, new_path)
if args.mark_deleted:
qbt_client.torrents_add_tags(tags="xklb-delete", torrent_hashes=torrent_hashes)
elif args.delete_files:
qbt_client.torrents_delete(delete_files=True, torrent_hashes=torrent_hashes)
return # nothing else can be done

# Start the shrink process (assuming a function or command to start shrink)
# Example: start_shrink_process(new_path)
print(f"Moved and started shrink process for {torrent_name}")
for torrent in torrents:
if args.move and os.path.exists(torrent.content_path):
new_path = Path(args.move)
if not new_path.is_absolute():
new_path = Path(path_utils.mountpoint(torrent.content_path)) / new_path

if args.tracker_dirnames:
tracker = torrent.tracker
if not tracker:
tracker = iterables.safe_unpack(
tr.url for tr in qbt_client.torrents_trackers(torrent.hash) if tr.url.startswith("http")
)
if tracker:
domain = path_utils.domain_from_url(tracker)
new_path /= domain

new_path.mkdir(parents=True, exist_ok=True)
log.info("Moving %s to %s", torrent.content_path, new_path)
shutil.move(torrent.content_path, new_path)

if args.delete_rows:
qbt_client.torrents_delete(delete_files=False, torrent_hashes=torrent.hash)
Loading

0 comments on commit 6e04b77

Please sign in to comment.