Coverage for seedboxsync/core/sync/services/blackhole.py: 89%

56 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-26 17:46 +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 as app, utils 

13from seedboxsync.core.dao 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 app.seedboxsync_config.get("sync_blackhole_enabled"): 

33 app.logger.info("Blackhole synchronization task is disabled") 

34 return 

35 

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

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

38 

39 # Call ping.start() if enabled 

40 if ping: 

41 app.ping.start("sync_blackhole") 

42 

43 # Gather all torrent files 

44 local_watch_path = app.seedboxsync_config.get("local_watch_path", "") 

45 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 app.logger.info('No torrent files found in "{}"'.format(app.seedboxsync_config.get("local_watch_path"))) 

50 # Call ping.success() if enabled 

51 if ping: 

52 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 app.logger.info(f'Dry-run: not uploading torrent "{torrent_name}"') 

61 continue 

62 

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

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

65 

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

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

68 

69 try: 

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

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

72 

73 # Apply chmod if configured 

74 chmod = app.seedboxsync_config.get("seedbox_chmod", False) 

75 if isinstance(chmod, str): 

76 app.logger.debug(f"Change permissions to {chmod}") 

77 app.sync.chmod(Path(tmp_path) / torrent_name, int(chmod, 8)) 

78 

79 # Move file from tmp to watch directory 

80 app.logger.debug(f'Move from "{tmp_path}" to "{watch_path}"') 

81 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_info = utils.get_torrent_infos(torrent_file) 

88 torrent = Torrent.create(name=torrent_name) 

89 if torrent_info is not None and isinstance(torrent_info, dict): 

90 torrent.announce = torrent_info.get("announce") 

91 torrent.save() 

92 

93 # Remove local torrent file 

94 app.logger.debug(f'Remove local torrent "{torrent_file}"') 

95 Path(torrent_file).unlink() 

96 else: 

97 app.logger.warning(f'Rename local "{torrent_file}" to .torrent.fail') 

98 Path(torrent_file).rename(fspath(torrent_file) + ".fail") 

99 except SSHException as exc: 

100 app.logger.warning(f"SSH client exception > {exc!s}") 

101 

102 # Call ping.success() if enabled 

103 if ping: 

104 app.ping.success("sync_blackhole")