-
Notifications
You must be signed in to change notification settings - Fork 452
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
303 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import logging | ||
import os | ||
from pathlib import Path | ||
|
||
from ipv8.taskmanager import TaskManager | ||
|
||
from tribler.core.libtorrent.torrentdef import TorrentDef | ||
from tribler.core.session import Session | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class WatchFolderManager: | ||
""" | ||
Watch the torrent files in a folder. | ||
Add torrents that are in this folder and remove torrents that are removed while we are watching. | ||
""" | ||
|
||
def __init__(self, session: Session, task_manager: TaskManager) -> None: | ||
""" | ||
Attach to the given task manager. | ||
""" | ||
super().__init__() | ||
self.session = session | ||
self.task_manager = task_manager | ||
|
||
def start(self) -> None: | ||
""" | ||
Start the periodic processing of the watch folder. | ||
""" | ||
update_interval = self.session.config.get("watch_folder/check_interval") | ||
self.task_manager.register_task("Watch Folder", self.check, interval=update_interval, delay=update_interval) | ||
|
||
def check(self) -> bool: | ||
""" | ||
Check the watch folder for new torrents and start downloading them. | ||
""" | ||
logger.debug("Checking watch folder...") | ||
str_directory = self.session.config.get("watch_folder/directory") | ||
if not str_directory: | ||
logger.debug("Cancelled. Directory: %s.", str_directory) | ||
return False | ||
|
||
path_directory = Path(str_directory).absolute() | ||
logger.info("Checking watch folder: %s", str(path_directory)) | ||
if not path_directory.exists(): | ||
logger.warning("Cancelled. Directory does not exist: %s.", str(path_directory)) | ||
return False | ||
|
||
if not path_directory.is_dir(): | ||
logger.warning("Cancelled. Is not directory: %s.", str(path_directory)) | ||
return False | ||
|
||
processed: set[Path] = set() | ||
for root, _, files in os.walk(str(path_directory)): | ||
for name in files: | ||
path = Path(root) / name | ||
processed.add(path) | ||
if not name.endswith(".torrent"): | ||
continue | ||
self.task_manager.replace_task(f"Process file {path!s}", self.process_torrent_file, path) | ||
|
||
logger.debug("Checking watch folder completed.") | ||
return True | ||
|
||
async def process_torrent_file(self, path: Path) -> None: | ||
""" | ||
Process an individual torrent file. | ||
""" | ||
logger.debug("Add watched torrent file: %s", str(path)) | ||
try: | ||
tdef = await TorrentDef.load(path) | ||
if not self.session.download_manager.download_exists(tdef.infohash): | ||
logger.info("Starting download from torrent file %s", path.name) | ||
await self.session.download_manager.start_download(torrent_file=path, tdef=tdef) | ||
except Exception as e: # pylint: disable=broad-except | ||
logger.exception("Exception while adding watched torrent! %s: %s", e.__class__.__name__, str(e)) |
Empty file.
160 changes: 160 additions & 0 deletions
160
src/tribler/test_unit/core/watch_folder/test_manager.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import os.path | ||
from asyncio import sleep | ||
from pathlib import Path | ||
from unittest.mock import AsyncMock, Mock, call, patch | ||
|
||
from ipv8.taskmanager import TaskManager | ||
from ipv8.test.base import TestBase | ||
|
||
from tribler.core.libtorrent.torrentdef import TorrentDef | ||
from tribler.core.watch_folder.manager import WatchFolderManager | ||
from tribler.test_unit.core.libtorrent.mocks import TORRENT_WITH_DIRS_CONTENT | ||
from tribler.tribler_config import TriblerConfigManager | ||
|
||
|
||
class MockTriblerConfigManager(TriblerConfigManager): | ||
""" | ||
A memory-based TriblerConfigManager. | ||
""" | ||
|
||
def write(self) -> None: | ||
""" | ||
Don't actually write to any file. | ||
""" | ||
|
||
|
||
class TestWatchFolderManager(TestBase): | ||
""" | ||
Tests for the Notifier class. | ||
""" | ||
|
||
def setUp(self) -> None: | ||
""" | ||
Create a new versioning manager. | ||
""" | ||
super().setUp() | ||
self.config = MockTriblerConfigManager() | ||
self.task_manager = TaskManager() | ||
self.manager = WatchFolderManager(Mock(config=self.config, download_manager=Mock( | ||
start_download=AsyncMock(), remove_download=AsyncMock())), self.task_manager) | ||
|
||
async def tearDown(self) -> None: | ||
""" | ||
Shut down our task manager. | ||
""" | ||
await self.task_manager.shutdown_task_manager() | ||
await super().tearDown() | ||
|
||
def test_watch_folder_not_dir(self) -> None: | ||
""" | ||
Test that the watch folder is disabled when the "directory" setting is not a directory. | ||
""" | ||
self.config.set("watch_folder/enabled", True) | ||
self.config.set("watch_folder/directory", "") | ||
|
||
result = self.manager.check() | ||
|
||
self.assertFalse(result) | ||
|
||
def test_watch_folder_invalid_dir(self) -> None: | ||
""" | ||
Test that the watch folder is disabled when the directory is invalid. | ||
""" | ||
self.config.set("watch_folder/enabled", True) | ||
self.config.set("watch_folder/directory", "BFLKJAELKJRLAKJDLAGKjLjgaEPGJAPEJGPAIJEPGIAPDJG") | ||
|
||
result = self.manager.check() | ||
|
||
self.assertFalse(result) | ||
|
||
async def test_watch_folder_no_files(self) -> None: | ||
""" | ||
Test that in the case of an empty folder, downloads are not started. | ||
""" | ||
self.config.set("watch_folder/enabled", True) | ||
self.config.set("watch_folder/directory", os.path.dirname(__file__)) | ||
|
||
with patch("os.walk", lambda _: []): | ||
result = self.manager.check() | ||
await sleep(0) | ||
scheduled_tasks = self.task_manager.get_tasks() | ||
|
||
self.assertTrue(result) | ||
self.assertEqual(0, len(scheduled_tasks)) | ||
|
||
async def test_watch_folder_no_torrent_file(self) -> None: | ||
""" | ||
Test that in the case of a folder without torrents, downloads are not started. | ||
""" | ||
self.config.set("watch_folder/enabled", True) | ||
self.config.set("watch_folder/directory", os.path.dirname(__file__)) | ||
|
||
result = self.manager.check() | ||
await sleep(0) | ||
scheduled_tasks = self.task_manager.get_tasks() | ||
|
||
self.assertTrue(result) | ||
self.assertEqual(0, len(scheduled_tasks)) | ||
|
||
async def test_watch_folder_torrent_file_start_download(self) -> None: | ||
""" | ||
Test that in the case of presence of a torrent file, a download is started. | ||
""" | ||
self.config.set("watch_folder/enabled", True) | ||
self.config.set("watch_folder/directory", os.path.dirname(__file__)) | ||
self.manager.session.download_manager.download_exists = lambda _: False | ||
tdef = TorrentDef.load_from_memory(TORRENT_WITH_DIRS_CONTENT) | ||
|
||
with patch("os.walk", lambda _: [(".", [], ["fake.torrent"])]), \ | ||
patch.object(TorrentDef, "load", AsyncMock(return_value=tdef)): | ||
result = self.manager.check() | ||
await sleep(0) # Schedule processing | ||
scheduled_tasks = self.task_manager.get_tasks() | ||
await sleep(0) # Process (i.e., start the download) | ||
|
||
self.assertTrue(result) | ||
self.assertEqual(1, len(scheduled_tasks)) | ||
self.assertEqual(call(torrent_file=Path("fake.torrent"), tdef=tdef), | ||
self.manager.session.download_manager.start_download.call_args) | ||
|
||
async def test_watch_folder_torrent_file_start_download_existing(self) -> None: | ||
""" | ||
Test that in the case of presence of a torrent file, a download is started twice. | ||
""" | ||
self.config.set("watch_folder/enabled", True) | ||
self.config.set("watch_folder/directory", os.path.dirname(__file__)) | ||
self.manager.session.download_manager.download_exists = lambda _: True | ||
tdef = TorrentDef.load_from_memory(TORRENT_WITH_DIRS_CONTENT) | ||
|
||
with patch("os.walk", lambda _: [(".", [], ["fake.torrent"])]), \ | ||
patch.object(TorrentDef, "load", AsyncMock(return_value=tdef)): | ||
result = self.manager.check() | ||
await sleep(0) # Schedule processing | ||
scheduled_tasks = self.task_manager.get_tasks() | ||
await sleep(0) # Process (i.e., start the download) | ||
|
||
self.assertTrue(result) | ||
self.assertEqual(1, len(scheduled_tasks)) | ||
self.assertIsNone(self.manager.session.download_manager.start_download.call_args) | ||
|
||
async def test_watch_folder_no_crash_exception(self) -> None: | ||
""" | ||
Test that errors raised during processing do not crash us. | ||
""" | ||
self.config.set("watch_folder/enabled", True) | ||
self.config.set("watch_folder/directory", os.path.dirname(__file__)) | ||
self.manager.session.download_manager.start_download = AsyncMock(side_effect=RuntimeError) | ||
self.manager.session.download_manager.download_exists = lambda _: False | ||
tdef = TorrentDef.load_from_memory(TORRENT_WITH_DIRS_CONTENT) | ||
|
||
with patch("os.walk", lambda _: [(".", [], ["fake.torrent"])]), \ | ||
patch.object(TorrentDef, "load", AsyncMock(return_value=tdef)): | ||
result = self.manager.check() | ||
await sleep(0) # Schedule processing | ||
scheduled_tasks = self.task_manager.get_tasks() | ||
await sleep(0) # Process (i.e., start the download) | ||
|
||
self.assertTrue(result) | ||
self.assertEqual(1, len(scheduled_tasks)) | ||
self.assertEqual(call(torrent_file=Path("fake.torrent"), tdef=tdef), | ||
self.manager.session.download_manager.start_download.call_args) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.