Coverage for seedboxsync/core/sync/services/blackhole.py: 100%
54 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-26 17:14 +0000
1#
2# Copyright (C) 2015-2026 Guillaume Kulakowski <guillaume@kulakowski.fr>
3#
4# For the full copyright and license information, please view the LICENSE
5# file that was distributed with this source code.
6#
7"""SeedboxSync sync service for blackhole."""
9from os import fspath
10from pathlib import Path
11from paramiko import SSHException
12from seedboxsync.core import current_app
13from seedboxsync.core.database.models import Torrent
14from seedboxsync.core.taskmanager import track_taskstatus
16LOCK_NAME = "sync-blackhole"
17PRIORITY = 10
20@track_taskstatus(LOCK_NAME)
21def blackhole(dry_run: bool, ping: bool) -> None:
22 """
23 Perform the blackhole synchronization.
25 Uploads torrent files from the local watch folder to the seedbox.
26 Optional dry-run, file permissions, database persistence, and error handling.
28 Args:
29 dry_run (bool): Whether to perform a dry run.
30 ping (bool): Whether to ping a service during execution.
31 """
32 if not current_app.seedboxsync_config.get("sync_blackhole_enabled"):
33 current_app.logger.info("Blackhole synchronization task is disabled")
34 return
36 current_app.logger.debug(f'sync blackhole dry-run: "{dry_run}"')
37 current_app.logger.debug(f'sync blackhole ping: "{ping}"')
39 # Call ping.start() if enabled
40 if ping:
41 current_app.ping.start("sync_blackhole")
43 # Gather all torrent files
44 local_watch_path = current_app.seedboxsync_config.get("local_watch_path", "")
45 current_app.logger.debug(f'Scanning for torrent files in "{local_watch_path}"')
46 torrents = list(Path(local_watch_path).expanduser().resolve().glob("*.torrent"))
48 if len(torrents) == 0:
49 current_app.logger.info('No torrent files found in "{}"'.format(current_app.seedboxsync_config.get("local_watch_path")))
50 # Call ping.success() if enabled
51 if ping:
52 current_app.ping.success("sync_blackhole")
53 return
55 for torrent_file in torrents:
56 torrent_name = torrent_file.name
58 # Dry-run mode
59 if dry_run:
60 current_app.logger.info(f'Dry-run: not uploading torrent "{torrent_name}"')
61 continue
63 tmp_path = current_app.seedboxsync_config.get("seedbox_tmp_path", "")
64 watch_path = current_app.seedboxsync_config.get("seedbox_watch_path", "")
66 current_app.logger.info(f'Upload torrent: "{torrent_name}"')
67 current_app.logger.debug(f'Upload "{torrent_file}" to "{tmp_path}"')
69 try:
70 current_app.sync.chdir(None) # type: ignore[arg-type]
71 current_app.sync.put(torrent_file, Path(tmp_path) / torrent_name)
73 # Apply chmod if configured
74 chmod = current_app.seedboxsync_config.get("seedbox_chmod", False)
75 if isinstance(chmod, str):
76 current_app.logger.debug(f"Change permissions to {chmod}")
77 current_app.sync.chmod(Path(tmp_path) / torrent_name, int(chmod, 8))
79 # Move file from tmp to watch directory
80 current_app.logger.debug(f'Move from "{tmp_path}" to "{watch_path}"')
81 current_app.sync.rename(
82 Path(tmp_path) / torrent_name,
83 Path(watch_path) / torrent_name,
84 )
86 # Store torrent info in database
87 torrent = Torrent.create(name=torrent_name)
88 if torrent.set_from_file(torrent_file):
89 torrent.save()
91 # Remove local torrent file
92 current_app.logger.debug(f'Remove local torrent "{torrent_file}"')
93 Path(torrent_file).unlink()
94 else:
95 current_app.logger.warning(f'Rename local "{torrent_file}" to .torrent.fail')
96 Path(torrent_file).rename(fspath(torrent_file) + ".fail")
97 except SSHException as exc:
98 current_app.logger.warning(f"SSH client exception > {exc!s}")
100 # Call ping.success() if enabled
101 if ping:
102 current_app.ping.success("sync_blackhole")