We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
 BUG: Unhandled error in plugin convert_magnet: 'save_path must be set in add_torrent_params'

### Expected behaviour:

<!---
Should be convert the magnet URI to torrent file
--->

### Actual behaviour:

### Steps to reproduce:
- Step 1: flexget execute 

#### Config:
```yaml
templates:
  torrent:
    transmission:
      host: localhost
      port: 9091
      username: XXXXX
      password: XXXXXX

  tv_showrss:
    all_series: yes
    convert_magnet: yes
    seen: local
    download: /mnt/kodi/complete/peliculas/
    template: torrent


  movies:
    template: torrent
    imdb:
      min_score: 5
      min_votes: 100
      min_year: 2020
      reject_genres:
        - horror
        - porn
        - anime
        - xxx
      accept_languages:
        - english
    imdb_lookup: yes
    unique:
      field:
        - imdb_id
        - movie_name
      action: reject
    seen: local
    convert_magnet: yes
    quality: 2160p
    download: /mnt/kodi/complete/peliculas/

tasks:
  series_task:
    rss:
      url: http://showrss.info/user/XXXXX.rss?magnets=true&namespaces=true&name=clean&quality=null&re=null
      escape: yes
      link:
        - link
        - magneturl
    template: tv_showrss
    retry_failed:
      retry_time: 30 minutes # Base time in between retries
      retry_time_multiplier: 2 # Amount retry time will be multiplied by after each successive failure
      max_retries: 5

  movies_task:
    rss:
      url: https://XXXXX.org/rssdd.php?categories=50;52
      escape: yes
      link:
        - link
        - magneturl
    template: movies
 

  move-episodes:
    metainfo_series: yes
    require_field: series_name
    accept_all: yes
    thetvdb_lookup: yes
    all_series:
      parse_only: yes
    filesystem:
      path: /mnt/kodi/complete/peliculas/
      regexp: '.*\.(avi|mkv|mp4|mpg)$'
      recursive: yes
    regexp:
      reject:
        - sample
    move:
      clean_source: 50
      to: '/mnt/kodi/complete/series/{{series_name}}/'
      clean_source: 1000
      along:
        extensions:
          - sub
          - srt
```
  
#### Log:
<details>
 <summary>(click to expand)</summary>

```
2023-05-28 12:27:57 CRITICAL task          movies_task     BUG: Unhandled error in plugin convert_magnet: 'save_path must be set in add_torrent_params'
Traceback (most recent call last):

  File "/usr/lib/python3.10/threading.py", line 973, in _bootstrap
    self._bootstrap_inner()
    │    └ <function Thread._bootstrap_inner at 0x7f6954bddb40>
    └ <Thread(task_queue, started daemon 140090266555968)>
  File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
    self.run()
    │    └ <function Thread.run at 0x7f6954bdd870>
    └ <Thread(task_queue, started daemon 140090266555968)>
  File "/usr/lib/python3.10/threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
    │    │        │    │        │    └ {}
    │    │        │    │        └ <Thread(task_queue, started daemon 140090266555968)>
    │    │        │    └ ()
    │    │        └ <Thread(task_queue, started daemon 140090266555968)>
    │    └ <bound method TaskQueue.run of <flexget.task_queue.TaskQueue object at 0x7f694ed09a50>>
    └ <Thread(task_queue, started daemon 140090266555968)>
  File "/usr/local/lib/python3.10/dist-packages/flexget/task_queue.py", line 46, in run
    self.current_task.execute()
    │    │            └ <function Task.execute at 0x7f6951d9dc60>
    │    └ <flexget.task.Task object at 0x7f694e9a7d30>
    └ <flexget.task_queue.TaskQueue object at 0x7f694ed09a50>
  File "/usr/local/lib/python3.10/dist-packages/flexget/task.py", line 87, in wrapper
    return func(self, *args, **kw)
           │    │      │       └ {}
           │    │      └ ()
           │    └ <flexget.task.Task object at 0x7f694e9a7d30>
           └ <function Task.execute at 0x7f6951d9dbd0>
  File "/usr/local/lib/python3.10/dist-packages/flexget/task.py", line 725, in execute
    self._execute()
    │    └ <function Task._execute at 0x7f6951d9db40>
    └ <flexget.task.Task object at 0x7f694e9a7d30>
  File "/usr/local/lib/python3.10/dist-packages/flexget/task.py", line 694, in _execute
    self.__run_task_phase(phase)
    │                     └ 'download'
    └ <flexget.task.Task object at 0x7f694e9a7d30>
  File "/usr/local/lib/python3.10/dist-packages/flexget/task.py", line 514, in __run_task_phase
    response = self.__run_plugin(plugin, phase, args)
               │                 │       │      └ (<flexget.task.Task object at 0x7f694e9a7d30>, True)
               │                 │       └ 'download'
               │                 └ <PluginInfo(name=convert_magnet)>
               └ <flexget.task.Task object at 0x7f694e9a7d30>
> File "/usr/local/lib/python3.10/dist-packages/flexget/task.py", line 547, in __run_plugin
    result = method(*args, **kwargs)
             │       │       └ {}
             │       └ (<flexget.task.Task object at 0x7f694e9a7d30>, True)
             └ <Event(name=plugin.convert_magnet.download,func=on_task_download,priority=130)>
  File "/usr/local/lib/python3.10/dist-packages/flexget/event.py", line 20, in __call__
    return self.func(*args, **kwargs)
           │    │     │       └ {}
           │    │     └ (<flexget.task.Task object at 0x7f694e9a7d30>, True)
           │    └ <bound method ConvertMagnet.on_task_download of <flexget.components.bittorrent.convert_magnet.ConvertMagnet object at 0x7f694...
           └ <Event(name=plugin.convert_magnet.download,func=on_task_download,priority=130)>
  File "/usr/local/lib/python3.10/dist-packages/flexget/components/bittorrent/convert_magnet.py", line 102, in on_task_download
    torrent_file = self.magnet_to_torrent(entry['url'], converted_path, timeout)
                   │    │                 │             │               └ 30.0
                   │    │                 │             └ '/home/wooltar/.flexget/converted'
                   │    │                 └ <Entry(title=Venom.Let.There.Be.Carnage.2021.2160p.UHD.BluRay.x265.10bit.HDR.TrueHD.7.1.Atmos-RARBG,state=accepted)>
                   │    └ <function ConvertMagnet.magnet_to_torrent at 0x7f69500f1900>
                   └ <flexget.components.bittorrent.convert_magnet.ConvertMagnet object at 0x7f694ecbee60>
  File "/usr/local/lib/python3.10/dist-packages/flexget/components/bittorrent/convert_magnet.py", line 47, in magnet_to_torrent
    handle = session.add_torrent(params)
             │       │           └ <libtorrent.add_torrent_params object at 0x7f69483267f0>
             │       └ <Boost.Python.function object at 0x7f6948036b80>
             └ <libtorrent.session object at 0x7f694ec13150>```
```
</details>

### Additional information:

- FlexGet version: 3.7.2
- Python version: 3.10.6
- Installation method: pip3 
- Using daemon (yes/no): no
- OS and version: Ubuntu 22.04.2 LTS
- Link to crash log:



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `flexget/components/bittorrent/convert_magnet.py`
Content:
```
1 import os
2 import time
3 from urllib.parse import quote
4 
5 from loguru import logger
6 
7 from flexget import plugin
8 from flexget.event import event
9 from flexget.utils.pathscrub import pathscrub
10 from flexget.utils.tools import parse_timedelta
11 
12 logger = logger.bind(name='convert_magnet')
13 
14 
15 class ConvertMagnet:
16     """Convert magnet only entries to a torrent file"""
17 
18     schema = {
19         "oneOf": [
20             # Allow convert_magnet: no form to turn off plugin altogether
21             {"type": "boolean"},
22             {
23                 "type": "object",
24                 "properties": {
25                     "timeout": {"type": "string", "format": "interval"},
26                     "force": {"type": "boolean"},
27                 },
28                 "additionalProperties": False,
29             },
30         ]
31     }
32 
33     def magnet_to_torrent(self, magnet_uri, destination_folder, timeout):
34         import libtorrent
35 
36         params = libtorrent.parse_magnet_uri(magnet_uri)
37         session = libtorrent.session()
38         lt_version = [int(v) for v in libtorrent.version.split('.')]
39         if lt_version > [0, 16, 13, 0] and lt_version < [1, 1, 3, 0]:
40             # for some reason the info_hash needs to be bytes but it's a struct called sha1_hash
41             params['info_hash'] = params['info_hash'].to_bytes()
42         if lt_version < [1, 2]:
43             # for versions < 1.2
44             params['url'] = magnet_uri
45         else:
46             params.url = magnet_uri
47         handle = session.add_torrent(params)
48         logger.debug('Acquiring torrent metadata for magnet {}', magnet_uri)
49         timeout_value = timeout
50         while not handle.has_metadata():
51             time.sleep(0.1)
52             timeout_value -= 0.1
53             if timeout_value <= 0:
54                 raise plugin.PluginError(f'Timed out after {timeout} seconds trying to magnetize')
55         logger.debug('Metadata acquired')
56         torrent_info = handle.get_torrent_info()
57         torrent_file = libtorrent.create_torrent(torrent_info)
58         torrent_path = pathscrub(
59             os.path.join(destination_folder, torrent_info.name() + ".torrent")
60         )
61         with open(torrent_path, "wb") as f:
62             f.write(libtorrent.bencode(torrent_file.generate()))
63         logger.debug('Torrent file wrote to {}', torrent_path)
64         return torrent_path
65 
66     def prepare_config(self, config):
67         if not isinstance(config, dict):
68             config = {}
69         config.setdefault('timeout', '30 seconds')
70         config.setdefault('force', False)
71         return config
72 
73     @plugin.priority(plugin.PRIORITY_FIRST)
74     def on_task_start(self, task, config):
75         if config is False:
76             return
77         try:
78             import libtorrent  # noqa
79         except ImportError:
80             raise plugin.DependencyError(
81                 'convert_magnet', 'libtorrent', 'libtorrent package required', logger
82             )
83 
84     @plugin.priority(130)
85     def on_task_download(self, task, config):
86         if config is False:
87             return
88         config = self.prepare_config(config)
89         # Create the conversion target directory
90         converted_path = os.path.join(task.manager.config_base, 'converted')
91 
92         timeout = parse_timedelta(config['timeout']).total_seconds()
93 
94         if not os.path.isdir(converted_path):
95             os.mkdir(converted_path)
96 
97         for entry in task.accepted:
98             if entry['url'].startswith('magnet:'):
99                 entry.setdefault('urls', [entry['url']])
100                 try:
101                     logger.info('Converting entry {} magnet URI to a torrent file', entry['title'])
102                     torrent_file = self.magnet_to_torrent(entry['url'], converted_path, timeout)
103                 except (plugin.PluginError, TypeError) as e:
104                     logger.error(
105                         'Unable to convert Magnet URI for entry {}: {}', entry['title'], e
106                     )
107                     if config['force']:
108                         entry.fail('Magnet URI conversion failed')
109                     continue
110                 # Windows paths need an extra / prepended to them for url
111                 if not torrent_file.startswith('/'):
112                     torrent_file = '/' + torrent_file
113                 entry['url'] = torrent_file
114                 entry['file'] = torrent_file
115                 # make sure it's first in the list because of how download plugin works
116                 entry['urls'].insert(0, f'file://{quote(torrent_file)}')
117 
118 
119 @event('plugin.register')
120 def register_plugin():
121     plugin.register(ConvertMagnet, 'convert_magnet', api_ver=2)
122
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/flexget/components/bittorrent/convert_magnet.py b/flexget/components/bittorrent/convert_magnet.py
--- a/flexget/components/bittorrent/convert_magnet.py
+++ b/flexget/components/bittorrent/convert_magnet.py
@@ -44,6 +44,7 @@
             params['url'] = magnet_uri
         else:
             params.url = magnet_uri
+        params.save_path = destination_folder
         handle = session.add_torrent(params)
         logger.debug('Acquiring torrent metadata for magnet {}', magnet_uri)
         timeout_value = timeout

{"golden_diff": "diff --git a/flexget/components/bittorrent/convert_magnet.py b/flexget/components/bittorrent/convert_magnet.py\n--- a/flexget/components/bittorrent/convert_magnet.py\n+++ b/flexget/components/bittorrent/convert_magnet.py\n@@ -44,6 +44,7 @@\n             params['url'] = magnet_uri\n         else:\n             params.url = magnet_uri\n+        params.save_path = destination_folder\n         handle = session.add_torrent(params)\n         logger.debug('Acquiring torrent metadata for magnet {}', magnet_uri)\n         timeout_value = timeout\n", "issue": " BUG: Unhandled error in plugin convert_magnet: 'save_path must be set in add_torrent_params'\n\r\n### Expected behaviour:\r\n\r\n<!---\r\nShould be convert the magnet URI to torrent file\r\n--->\r\n\r\n### Actual behaviour:\r\n\r\n### Steps to reproduce:\r\n- Step 1: flexget execute \r\n\r\n#### Config:\r\n```yaml\r\ntemplates:\r\n  torrent:\r\n    transmission:\r\n      host: localhost\r\n      port: 9091\r\n      username: XXXXX\r\n      password: XXXXXX\r\n\r\n  tv_showrss:\r\n    all_series: yes\r\n    convert_magnet: yes\r\n    seen: local\r\n    download: /mnt/kodi/complete/peliculas/\r\n    template: torrent\r\n\r\n\r\n  movies:\r\n    template: torrent\r\n    imdb:\r\n      min_score: 5\r\n      min_votes: 100\r\n      min_year: 2020\r\n      reject_genres:\r\n        - horror\r\n        - porn\r\n        - anime\r\n        - xxx\r\n      accept_languages:\r\n        - english\r\n    imdb_lookup: yes\r\n    unique:\r\n      field:\r\n        - imdb_id\r\n        - movie_name\r\n      action: reject\r\n    seen: local\r\n    convert_magnet: yes\r\n    quality: 2160p\r\n    download: /mnt/kodi/complete/peliculas/\r\n\r\ntasks:\r\n  series_task:\r\n    rss:\r\n      url: http://showrss.info/user/XXXXX.rss?magnets=true&namespaces=true&name=clean&quality=null&re=null\r\n      escape: yes\r\n      link:\r\n        - link\r\n        - magneturl\r\n    template: tv_showrss\r\n    retry_failed:\r\n      retry_time: 30 minutes # Base time in between retries\r\n      retry_time_multiplier: 2 # Amount retry time will be multiplied by after each successive failure\r\n      max_retries: 5\r\n\r\n  movies_task:\r\n    rss:\r\n      url: https://XXXXX.org/rssdd.php?categories=50;52\r\n      escape: yes\r\n      link:\r\n        - link\r\n        - magneturl\r\n    template: movies\r\n \r\n\r\n  move-episodes:\r\n    metainfo_series: yes\r\n    require_field: series_name\r\n    accept_all: yes\r\n    thetvdb_lookup: yes\r\n    all_series:\r\n      parse_only: yes\r\n    filesystem:\r\n      path: /mnt/kodi/complete/peliculas/\r\n      regexp: '.*\\.(avi|mkv|mp4|mpg)$'\r\n      recursive: yes\r\n    regexp:\r\n      reject:\r\n        - sample\r\n    move:\r\n      clean_source: 50\r\n      to: '/mnt/kodi/complete/series/{{series_name}}/'\r\n      clean_source: 1000\r\n      along:\r\n        extensions:\r\n          - sub\r\n          - srt\r\n```\r\n  \r\n#### Log:\r\n<details>\r\n <summary>(click to expand)</summary>\r\n\r\n```\r\n2023-05-28 12:27:57 CRITICAL task          movies_task     BUG: Unhandled error in plugin convert_magnet: 'save_path must be set in add_torrent_params'\r\nTraceback (most recent call last):\r\n\r\n  File \"/usr/lib/python3.10/threading.py\", line 973, in _bootstrap\r\n    self._bootstrap_inner()\r\n    \u2502    \u2514 <function Thread._bootstrap_inner at 0x7f6954bddb40>\r\n    \u2514 <Thread(task_queue, started daemon 140090266555968)>\r\n  File \"/usr/lib/python3.10/threading.py\", line 1016, in _bootstrap_inner\r\n    self.run()\r\n    \u2502    \u2514 <function Thread.run at 0x7f6954bdd870>\r\n    \u2514 <Thread(task_queue, started daemon 140090266555968)>\r\n  File \"/usr/lib/python3.10/threading.py\", line 953, in run\r\n    self._target(*self._args, **self._kwargs)\r\n    \u2502    \u2502        \u2502    \u2502        \u2502    \u2514 {}\r\n    \u2502    \u2502        \u2502    \u2502        \u2514 <Thread(task_queue, started daemon 140090266555968)>\r\n    \u2502    \u2502        \u2502    \u2514 ()\r\n    \u2502    \u2502        \u2514 <Thread(task_queue, started daemon 140090266555968)>\r\n    \u2502    \u2514 <bound method TaskQueue.run of <flexget.task_queue.TaskQueue object at 0x7f694ed09a50>>\r\n    \u2514 <Thread(task_queue, started daemon 140090266555968)>\r\n  File \"/usr/local/lib/python3.10/dist-packages/flexget/task_queue.py\", line 46, in run\r\n    self.current_task.execute()\r\n    \u2502    \u2502            \u2514 <function Task.execute at 0x7f6951d9dc60>\r\n    \u2502    \u2514 <flexget.task.Task object at 0x7f694e9a7d30>\r\n    \u2514 <flexget.task_queue.TaskQueue object at 0x7f694ed09a50>\r\n  File \"/usr/local/lib/python3.10/dist-packages/flexget/task.py\", line 87, in wrapper\r\n    return func(self, *args, **kw)\r\n           \u2502    \u2502      \u2502       \u2514 {}\r\n           \u2502    \u2502      \u2514 ()\r\n           \u2502    \u2514 <flexget.task.Task object at 0x7f694e9a7d30>\r\n           \u2514 <function Task.execute at 0x7f6951d9dbd0>\r\n  File \"/usr/local/lib/python3.10/dist-packages/flexget/task.py\", line 725, in execute\r\n    self._execute()\r\n    \u2502    \u2514 <function Task._execute at 0x7f6951d9db40>\r\n    \u2514 <flexget.task.Task object at 0x7f694e9a7d30>\r\n  File \"/usr/local/lib/python3.10/dist-packages/flexget/task.py\", line 694, in _execute\r\n    self.__run_task_phase(phase)\r\n    \u2502                     \u2514 'download'\r\n    \u2514 <flexget.task.Task object at 0x7f694e9a7d30>\r\n  File \"/usr/local/lib/python3.10/dist-packages/flexget/task.py\", line 514, in __run_task_phase\r\n    response = self.__run_plugin(plugin, phase, args)\r\n               \u2502                 \u2502       \u2502      \u2514 (<flexget.task.Task object at 0x7f694e9a7d30>, True)\r\n               \u2502                 \u2502       \u2514 'download'\r\n               \u2502                 \u2514 <PluginInfo(name=convert_magnet)>\r\n               \u2514 <flexget.task.Task object at 0x7f694e9a7d30>\r\n> File \"/usr/local/lib/python3.10/dist-packages/flexget/task.py\", line 547, in __run_plugin\r\n    result = method(*args, **kwargs)\r\n             \u2502       \u2502       \u2514 {}\r\n             \u2502       \u2514 (<flexget.task.Task object at 0x7f694e9a7d30>, True)\r\n             \u2514 <Event(name=plugin.convert_magnet.download,func=on_task_download,priority=130)>\r\n  File \"/usr/local/lib/python3.10/dist-packages/flexget/event.py\", line 20, in __call__\r\n    return self.func(*args, **kwargs)\r\n           \u2502    \u2502     \u2502       \u2514 {}\r\n           \u2502    \u2502     \u2514 (<flexget.task.Task object at 0x7f694e9a7d30>, True)\r\n           \u2502    \u2514 <bound method ConvertMagnet.on_task_download of <flexget.components.bittorrent.convert_magnet.ConvertMagnet object at 0x7f694...\r\n           \u2514 <Event(name=plugin.convert_magnet.download,func=on_task_download,priority=130)>\r\n  File \"/usr/local/lib/python3.10/dist-packages/flexget/components/bittorrent/convert_magnet.py\", line 102, in on_task_download\r\n    torrent_file = self.magnet_to_torrent(entry['url'], converted_path, timeout)\r\n                   \u2502    \u2502                 \u2502             \u2502               \u2514 30.0\r\n                   \u2502    \u2502                 \u2502             \u2514 '/home/wooltar/.flexget/converted'\r\n                   \u2502    \u2502                 \u2514 <Entry(title=Venom.Let.There.Be.Carnage.2021.2160p.UHD.BluRay.x265.10bit.HDR.TrueHD.7.1.Atmos-RARBG,state=accepted)>\r\n                   \u2502    \u2514 <function ConvertMagnet.magnet_to_torrent at 0x7f69500f1900>\r\n                   \u2514 <flexget.components.bittorrent.convert_magnet.ConvertMagnet object at 0x7f694ecbee60>\r\n  File \"/usr/local/lib/python3.10/dist-packages/flexget/components/bittorrent/convert_magnet.py\", line 47, in magnet_to_torrent\r\n    handle = session.add_torrent(params)\r\n             \u2502       \u2502           \u2514 <libtorrent.add_torrent_params object at 0x7f69483267f0>\r\n             \u2502       \u2514 <Boost.Python.function object at 0x7f6948036b80>\r\n             \u2514 <libtorrent.session object at 0x7f694ec13150>```\r\n```\r\n</details>\r\n\r\n### Additional information:\r\n\r\n- FlexGet version: 3.7.2\r\n- Python version: 3.10.6\r\n- Installation method: pip3 \r\n- Using daemon (yes/no): no\r\n- OS and version: Ubuntu 22.04.2 LTS\r\n- Link to crash log:\r\n\r\n\n", "before_files": [{"content": "import os\nimport time\nfrom urllib.parse import quote\n\nfrom loguru import logger\n\nfrom flexget import plugin\nfrom flexget.event import event\nfrom flexget.utils.pathscrub import pathscrub\nfrom flexget.utils.tools import parse_timedelta\n\nlogger = logger.bind(name='convert_magnet')\n\n\nclass ConvertMagnet:\n    \"\"\"Convert magnet only entries to a torrent file\"\"\"\n\n    schema = {\n        \"oneOf\": [\n            # Allow convert_magnet: no form to turn off plugin altogether\n            {\"type\": \"boolean\"},\n            {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"timeout\": {\"type\": \"string\", \"format\": \"interval\"},\n                    \"force\": {\"type\": \"boolean\"},\n                },\n                \"additionalProperties\": False,\n            },\n        ]\n    }\n\n    def magnet_to_torrent(self, magnet_uri, destination_folder, timeout):\n        import libtorrent\n\n        params = libtorrent.parse_magnet_uri(magnet_uri)\n        session = libtorrent.session()\n        lt_version = [int(v) for v in libtorrent.version.split('.')]\n        if lt_version > [0, 16, 13, 0] and lt_version < [1, 1, 3, 0]:\n            # for some reason the info_hash needs to be bytes but it's a struct called sha1_hash\n            params['info_hash'] = params['info_hash'].to_bytes()\n        if lt_version < [1, 2]:\n            # for versions < 1.2\n            params['url'] = magnet_uri\n        else:\n            params.url = magnet_uri\n        handle = session.add_torrent(params)\n        logger.debug('Acquiring torrent metadata for magnet {}', magnet_uri)\n        timeout_value = timeout\n        while not handle.has_metadata():\n            time.sleep(0.1)\n            timeout_value -= 0.1\n            if timeout_value <= 0:\n                raise plugin.PluginError(f'Timed out after {timeout} seconds trying to magnetize')\n        logger.debug('Metadata acquired')\n        torrent_info = handle.get_torrent_info()\n        torrent_file = libtorrent.create_torrent(torrent_info)\n        torrent_path = pathscrub(\n            os.path.join(destination_folder, torrent_info.name() + \".torrent\")\n        )\n        with open(torrent_path, \"wb\") as f:\n            f.write(libtorrent.bencode(torrent_file.generate()))\n        logger.debug('Torrent file wrote to {}', torrent_path)\n        return torrent_path\n\n    def prepare_config(self, config):\n        if not isinstance(config, dict):\n            config = {}\n        config.setdefault('timeout', '30 seconds')\n        config.setdefault('force', False)\n        return config\n\n    @plugin.priority(plugin.PRIORITY_FIRST)\n    def on_task_start(self, task, config):\n        if config is False:\n            return\n        try:\n            import libtorrent  # noqa\n        except ImportError:\n            raise plugin.DependencyError(\n                'convert_magnet', 'libtorrent', 'libtorrent package required', logger\n            )\n\n    @plugin.priority(130)\n    def on_task_download(self, task, config):\n        if config is False:\n            return\n        config = self.prepare_config(config)\n        # Create the conversion target directory\n        converted_path = os.path.join(task.manager.config_base, 'converted')\n\n        timeout = parse_timedelta(config['timeout']).total_seconds()\n\n        if not os.path.isdir(converted_path):\n            os.mkdir(converted_path)\n\n        for entry in task.accepted:\n            if entry['url'].startswith('magnet:'):\n                entry.setdefault('urls', [entry['url']])\n                try:\n                    logger.info('Converting entry {} magnet URI to a torrent file', entry['title'])\n                    torrent_file = self.magnet_to_torrent(entry['url'], converted_path, timeout)\n                except (plugin.PluginError, TypeError) as e:\n                    logger.error(\n                        'Unable to convert Magnet URI for entry {}: {}', entry['title'], e\n                    )\n                    if config['force']:\n                        entry.fail('Magnet URI conversion failed')\n                    continue\n                # Windows paths need an extra / prepended to them for url\n                if not torrent_file.startswith('/'):\n                    torrent_file = '/' + torrent_file\n                entry['url'] = torrent_file\n                entry['file'] = torrent_file\n                # make sure it's first in the list because of how download plugin works\n                entry['urls'].insert(0, f'file://{quote(torrent_file)}')\n\n\n@event('plugin.register')\ndef register_plugin():\n    plugin.register(ConvertMagnet, 'convert_magnet', api_ver=2)\n", "path": "flexget/components/bittorrent/convert_magnet.py"}], "after_files": [{"content": "import os\nimport time\nfrom urllib.parse import quote\n\nfrom loguru import logger\n\nfrom flexget import plugin\nfrom flexget.event import event\nfrom flexget.utils.pathscrub import pathscrub\nfrom flexget.utils.tools import parse_timedelta\n\nlogger = logger.bind(name='convert_magnet')\n\n\nclass ConvertMagnet:\n    \"\"\"Convert magnet only entries to a torrent file\"\"\"\n\n    schema = {\n        \"oneOf\": [\n            # Allow convert_magnet: no form to turn off plugin altogether\n            {\"type\": \"boolean\"},\n            {\n                \"type\": \"object\",\n                \"properties\": {\n                    \"timeout\": {\"type\": \"string\", \"format\": \"interval\"},\n                    \"force\": {\"type\": \"boolean\"},\n                },\n                \"additionalProperties\": False,\n            },\n        ]\n    }\n\n    def magnet_to_torrent(self, magnet_uri, destination_folder, timeout):\n        import libtorrent\n\n        params = libtorrent.parse_magnet_uri(magnet_uri)\n        session = libtorrent.session()\n        lt_version = [int(v) for v in libtorrent.version.split('.')]\n        if lt_version > [0, 16, 13, 0] and lt_version < [1, 1, 3, 0]:\n            # for some reason the info_hash needs to be bytes but it's a struct called sha1_hash\n            params['info_hash'] = params['info_hash'].to_bytes()\n        if lt_version < [1, 2]:\n            # for versions < 1.2\n            params['url'] = magnet_uri\n        else:\n            params.url = magnet_uri\n        params.save_path = destination_folder\n        handle = session.add_torrent(params)\n        logger.debug('Acquiring torrent metadata for magnet {}', magnet_uri)\n        timeout_value = timeout\n        while not handle.has_metadata():\n            time.sleep(0.1)\n            timeout_value -= 0.1\n            if timeout_value <= 0:\n                raise plugin.PluginError(f'Timed out after {timeout} seconds trying to magnetize')\n        logger.debug('Metadata acquired')\n        torrent_info = handle.get_torrent_info()\n        torrent_file = libtorrent.create_torrent(torrent_info)\n        torrent_path = pathscrub(\n            os.path.join(destination_folder, torrent_info.name() + \".torrent\")\n        )\n        with open(torrent_path, \"wb\") as f:\n            f.write(libtorrent.bencode(torrent_file.generate()))\n        logger.debug('Torrent file wrote to {}', torrent_path)\n        return torrent_path\n\n    def prepare_config(self, config):\n        if not isinstance(config, dict):\n            config = {}\n        config.setdefault('timeout', '30 seconds')\n        config.setdefault('force', False)\n        return config\n\n    @plugin.priority(plugin.PRIORITY_FIRST)\n    def on_task_start(self, task, config):\n        if config is False:\n            return\n        try:\n            import libtorrent  # noqa\n        except ImportError:\n            raise plugin.DependencyError(\n                'convert_magnet', 'libtorrent', 'libtorrent package required', logger\n            )\n\n    @plugin.priority(130)\n    def on_task_download(self, task, config):\n        if config is False:\n            return\n        config = self.prepare_config(config)\n        # Create the conversion target directory\n        converted_path = os.path.join(task.manager.config_base, 'converted')\n\n        timeout = parse_timedelta(config['timeout']).total_seconds()\n\n        if not os.path.isdir(converted_path):\n            os.mkdir(converted_path)\n\n        for entry in task.accepted:\n            if entry['url'].startswith('magnet:'):\n                entry.setdefault('urls', [entry['url']])\n                try:\n                    logger.info('Converting entry {} magnet URI to a torrent file', entry['title'])\n                    torrent_file = self.magnet_to_torrent(entry['url'], converted_path, timeout)\n                except (plugin.PluginError, TypeError) as e:\n                    logger.error(\n                        'Unable to convert Magnet URI for entry {}: {}', entry['title'], e\n                    )\n                    if config['force']:\n                        entry.fail('Magnet URI conversion failed')\n                    continue\n                # Windows paths need an extra / prepended to them for url\n                if not torrent_file.startswith('/'):\n                    torrent_file = '/' + torrent_file\n                entry['url'] = torrent_file\n                entry['file'] = torrent_file\n                # make sure it's first in the list because of how download plugin works\n                entry['urls'].insert(0, f'file://{quote(torrent_file)}')\n\n\n@event('plugin.register')\ndef register_plugin():\n    plugin.register(ConvertMagnet, 'convert_magnet', api_ver=2)\n", "path": "flexget/components/bittorrent/convert_magnet.py"}]}