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

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.""" 

8 

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 

15 

16LOCK_NAME = "sync-blackhole" 

17PRIORITY = 10 

18 

19 

20@track_taskstatus(LOCK_NAME) 

21def blackhole(dry_run: bool, ping: bool) -> None: 

22 """ 

23 Perform the blackhole synchronization. 

24 

25 Uploads torrent files from the local watch folder to the seedbox. 

26 Optional dry-run, file permissions, database persistence, and error handling. 

27 

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 

35 

36 current_app.logger.debug(f'sync blackhole dry-run: "{dry_run}"') 

37 current_app.logger.debug(f'sync blackhole ping: "{ping}"') 

38 

39 # Call ping.start() if enabled 

40 if ping: 

41 current_app.ping.start("sync_blackhole") 

42 

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")) 

47 

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 

54 

55 for torrent_file in torrents: 

56 torrent_name = torrent_file.name 

57 

58 # Dry-run mode 

59 if dry_run: 

60 current_app.logger.info(f'Dry-run: not uploading torrent "{torrent_name}"') 

61 continue 

62 

63 tmp_path = current_app.seedboxsync_config.get("seedbox_tmp_path", "") 

64 watch_path = current_app.seedboxsync_config.get("seedbox_watch_path", "") 

65 

66 current_app.logger.info(f'Upload torrent: "{torrent_name}"') 

67 current_app.logger.debug(f'Upload "{torrent_file}" to "{tmp_path}"') 

68 

69 try: 

70 current_app.sync.chdir(None) # type: ignore[arg-type] 

71 current_app.sync.put(torrent_file, Path(tmp_path) / torrent_name) 

72 

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)) 

78 

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 ) 

85 

86 # Store torrent info in database 

87 torrent = Torrent.create(name=torrent_name) 

88 if torrent.set_from_file(torrent_file): 

89 torrent.save() 

90 

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}") 

99 

100 # Call ping.success() if enabled 

101 if ping: 

102 current_app.ping.success("sync_blackhole")