source: deluge/core/core.py@ 7424cf

2.0.x develop
Last change on this file since 7424cf was 7424cf, checked in by Calum Lind <calumlind+deluge@gmail.com>, 12 years ago

Fix error in previous commit (b6a3161) and added test

  • Property mode set to 100644
File size: 32.1 KB
Line 
1#
2# core.py
3#
4# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
5# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
6#
7# Deluge is free software.
8#
9# You may redistribute it and/or modify it under the terms of the
10# GNU General Public License, as published by the Free Software
11# Foundation; either version 3 of the License, or (at your option)
12# any later version.
13#
14# deluge is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17# See the GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with deluge. If not, write to:
21# The Free Software Foundation, Inc.,
22# 51 Franklin Street, Fifth Floor
23# Boston, MA 02110-1301, USA.
24#
25# In addition, as a special exception, the copyright holders give
26# permission to link the code of portions of this program with the OpenSSL
27# library.
28# You must obey the GNU General Public License in all respects for all of
29# the code used other than OpenSSL. If you modify file(s) with this
30# exception, you may extend this exception to your version of the file(s),
31# but you are not obligated to do so. If you do not wish to do so, delete
32# this exception statement from your version. If you delete this exception
33# statement from all source files in the program, then also delete it here.
34#
35#
36
37from deluge._libtorrent import lt
38
39import os
40import glob
41import base64
42import logging
43import threading
44import tempfile
45from urlparse import urljoin
46
47import twisted.web.client
48import twisted.web.error
49
50from deluge.httpdownloader import download_file
51
52import deluge.configmanager
53import deluge.common
54import deluge.component as component
55from deluge.event import *
56from deluge.error import *
57from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AUTH_LEVEL_NONE
58from deluge.core.authmanager import AUTH_LEVELS_MAPPING, AUTH_LEVELS_MAPPING_REVERSE
59from deluge.core.torrentmanager import TorrentManager
60from deluge.core.pluginmanager import PluginManager
61from deluge.core.alertmanager import AlertManager
62from deluge.core.filtermanager import FilterManager
63from deluge.core.preferencesmanager import PreferencesManager
64from deluge.core.authmanager import AuthManager
65from deluge.core.eventmanager import EventManager
66from deluge.core.rpcserver import export
67
68log = logging.getLogger(__name__)
69
70class Core(component.Component):
71 def __init__(self, listen_interface=None):
72 log.debug("Core init..")
73 component.Component.__init__(self, "Core")
74
75 # Start the libtorrent session
76 log.info("Starting libtorrent %s session..", lt.version)
77
78 # Create the client fingerprint
79 version = deluge.common.VersionSplit(deluge.common.get_version()).version
80 while len(version) < 4:
81 version.append(0)
82
83 self.session = lt.session(lt.fingerprint("DE", *version), flags=0)
84
85 # Load the session state if available
86 self.__load_session_state()
87
88 # Set the user agent
89 self.settings = lt.session_settings()
90 self.settings.user_agent = "Deluge/%(deluge_version)s Libtorrent/%(lt_version)s" % \
91 { 'deluge_version': deluge.common.get_version(),
92 'lt_version': self.get_libtorrent_version().rpartition(".")[0] }
93 # Increase the alert queue size so that alerts don't get lost
94 self.settings.alert_queue_size = 10000
95
96 # Set session settings
97 self.settings.send_redundant_have = True
98 if deluge.common.windows_check():
99 self.settings.disk_io_write_mode = \
100 lt.io_buffer_mode_t.disable_os_cache
101 self.settings.disk_io_read_mode = \
102 lt.io_buffer_mode_t.disable_os_cache
103 self.session.set_settings(self.settings)
104
105 self.session.add_extension("metadata_transfer")
106 self.session.add_extension("ut_metadata")
107 self.session.add_extension("smart_ban")
108
109 # Create the components
110 self.eventmanager = EventManager()
111 self.preferencesmanager = PreferencesManager()
112 self.alertmanager = AlertManager()
113 self.pluginmanager = PluginManager(self)
114 self.torrentmanager = TorrentManager()
115 self.filtermanager = FilterManager(self)
116 self.authmanager = AuthManager()
117
118 # New release check information
119 self.new_release = None
120
121 # Get the core config
122 self.config = deluge.configmanager.ConfigManager("core.conf")
123 self.config.save()
124
125 # If there was an interface value from the command line, use it, but
126 # store the one in the config so we can restore it on shutdown
127 self.__old_interface = None
128 if listen_interface:
129 self.__old_interface = self.config["listen_interface"]
130 self.config["listen_interface"] = listen_interface
131
132 def start(self):
133 """Starts the core"""
134 # New release check information
135 self.__new_release = None
136
137 def stop(self):
138 log.debug("Core stopping...")
139
140 # Save the DHT state if necessary
141 if self.config["dht"]:
142 self.save_dht_state()
143
144 # Save the libtorrent session state
145 self.__save_session_state()
146
147 # We stored a copy of the old interface value
148 if self.__old_interface:
149 self.config["listen_interface"] = self.__old_interface
150
151 # Make sure the config file has been saved
152 self.config.save()
153
154 def shutdown(self):
155 pass
156
157 def __save_session_state(self):
158 """Saves the libtorrent session state"""
159 try:
160 session_state = deluge.configmanager.get_config_dir("session.state")
161 open(session_state, "wb").write(lt.bencode(self.session.save_state()))
162 except Exception, e:
163 log.warning("Failed to save lt state: %s", e)
164
165 def __load_session_state(self):
166 """Loads the libtorrent session state"""
167 try:
168 session_state = deluge.configmanager.get_config_dir("session.state")
169 self.session.load_state(lt.bdecode(open(session_state, "rb").read()))
170 except Exception, e:
171 log.warning("Failed to load lt state: %s", e)
172
173 def save_dht_state(self):
174 """Saves the dht state to a file"""
175 try:
176 dht_data = open(deluge.configmanager.get_config_dir("dht.state"), "wb")
177 dht_data.write(lt.bencode(self.session.dht_state()))
178 dht_data.close()
179 except Exception, e:
180 log.warning("Failed to save dht state: %s", e)
181
182 def get_new_release(self):
183 log.debug("get_new_release")
184 from urllib2 import urlopen
185 try:
186 self.new_release = urlopen(
187 "http://download.deluge-torrent.org/version-1.0").read().strip()
188 except Exception, e:
189 log.debug("Unable to get release info from website: %s", e)
190 return
191 self.check_new_release()
192
193 def check_new_release(self):
194 if self.new_release:
195 log.debug("new_release: %s", self.new_release)
196 if deluge.common.VersionSplit(self.new_release) > deluge.common.VersionSplit(deluge.common.get_version()):
197 component.get("EventManager").emit(NewVersionAvailableEvent(self.new_release))
198 return self.new_release
199 return False
200
201 # Exported Methods
202 @export
203 def add_torrent_file(self, filename, filedump, options):
204 """
205 Adds a torrent file to the session.
206
207 :param filename: the filename of the torrent
208 :type filename: string
209 :param filedump: a base64 encoded string of the torrent file contents
210 :type filedump: string
211 :param options: the options to apply to the torrent on add
212 :type options: dict
213
214 :returns: the torrent_id as a str or None
215 :rtype: string
216
217 """
218 try:
219 filedump = base64.decodestring(filedump)
220 except Exception, e:
221 log.error("There was an error decoding the filedump string!")
222 log.exception(e)
223
224 try:
225 torrent_id = self.torrentmanager.add(
226 filedump=filedump, options=options, filename=filename
227 )
228 except Exception, e:
229 log.error("There was an error adding the torrent file %s", filename)
230 log.exception(e)
231 torrent_id = None
232
233 return torrent_id
234
235 @export
236 def add_torrent_url(self, url, options, headers=None):
237 """
238 Adds a torrent from a url. Deluge will attempt to fetch the torrent
239 from url prior to adding it to the session.
240
241 :param url: the url pointing to the torrent file
242 :type url: string
243 :param options: the options to apply to the torrent on add
244 :type options: dict
245 :param headers: any optional headers to send
246 :type headers: dict
247
248 :returns: a Deferred which returns the torrent_id as a str or None
249 """
250 log.info("Attempting to add url %s", url)
251 def on_download_success(filename):
252 # We got the file, so add it to the session
253 f = open(filename, "rb")
254 data = f.read()
255 f.close()
256 try:
257 os.remove(filename)
258 except Exception, e:
259 log.warning("Couldn't remove temp file: %s", e)
260 return self.add_torrent_file(
261 filename, base64.encodestring(data), options
262 )
263
264 def on_download_fail(failure):
265 if failure.check(twisted.web.error.PageRedirect):
266 new_url = urljoin(url, failure.getErrorMessage().split(" to ")[1])
267 result = download_file(
268 new_url, tempfile.mkstemp()[1], headers=headers,
269 force_filename=True
270 )
271 result.addCallbacks(on_download_success, on_download_fail)
272 elif failure.check(twisted.web.client.PartialDownloadError):
273 result = download_file(
274 url, tempfile.mkstemp()[1], headers=headers,
275 force_filename=True, allow_compression=False
276 )
277 result.addCallbacks(on_download_success, on_download_fail)
278 else:
279 # Log the error and pass the failure onto the client
280 log.error("Error occured downloading torrent from %s", url)
281 log.error("Reason: %s", failure.getErrorMessage())
282 result = failure
283 return result
284
285 d = download_file(
286 url, tempfile.mkstemp()[1], headers=headers, force_filename=True
287 )
288 d.addCallbacks(on_download_success, on_download_fail)
289 return d
290
291 @export
292 def add_torrent_magnet(self, uri, options):
293 """
294 Adds a torrent from a magnet link.
295
296 :param uri: the magnet link
297 :type uri: string
298 :param options: the options to apply to the torrent on add
299 :type options: dict
300
301 :returns: the torrent_id
302 :rtype: string
303
304 """
305 log.debug("Attempting to add by magnet uri: %s", uri)
306
307 return self.torrentmanager.add(magnet=uri, options=options)
308
309 @export
310 def remove_torrent(self, torrent_id, remove_data):
311 """
312 Removes a torrent from the session.
313
314 :param torrent_id: the torrent_id of the torrent to remove
315 :type torrent_id: string
316 :param remove_data: if True, remove the data associated with this torrent
317 :type remove_data: boolean
318 :returns: True if removed successfully
319 :rtype: bool
320
321 :raises InvalidTorrentError: if the torrent_id does not exist in the session
322
323 """
324 log.debug("Removing torrent %s from the core.", torrent_id)
325 return self.torrentmanager.remove(torrent_id, remove_data)
326
327 @export
328 def get_session_status(self, keys):
329 """
330 Gets the session status values for 'keys', these keys are taking
331 from libtorrent's session status.
332
333 See: http://www.rasterbar.com/products/libtorrent/manual.html#status
334
335 :param keys: the keys for which we want values
336 :type keys: list
337 :returns: a dictionary of {key: value, ...}
338 :rtype: dict
339
340 """
341 status = {}
342 session_status = self.session.status()
343 for key in keys:
344 status[key] = getattr(session_status, key)
345
346 return status
347
348 @export
349 def get_cache_status(self):
350 """
351 Returns a dictionary of the session's cache status.
352
353 :returns: the cache status
354 :rtype: dict
355
356 """
357
358 status = self.session.get_cache_status()
359 cache = {}
360 for attr in dir(status):
361 if attr.startswith("_"):
362 continue
363 cache[attr] = getattr(status, attr)
364
365 # Add in a couple ratios
366 try:
367 cache["write_hit_ratio"] = float((cache["blocks_written"] - cache["writes"])) / float(cache["blocks_written"])
368 except ZeroDivisionError:
369 cache["write_hit_ratio"] = 0.0
370
371 try:
372 cache["read_hit_ratio"] = float(cache["blocks_read_hit"]) / float(cache["blocks_read"])
373 except ZeroDivisionError:
374 cache["read_hit_ratio"] = 0.0
375
376 return cache
377
378 @export
379 def force_reannounce(self, torrent_ids):
380 log.debug("Forcing reannouncment to: %s", torrent_ids)
381 for torrent_id in torrent_ids:
382 self.torrentmanager[torrent_id].force_reannounce()
383
384 @export
385 def pause_torrent(self, torrent_ids):
386 log.debug("Pausing: %s", torrent_ids)
387 for torrent_id in torrent_ids:
388 if not self.torrentmanager[torrent_id].pause():
389 log.warning("Error pausing torrent %s", torrent_id)
390
391 @export
392 def connect_peer(self, torrent_id, ip, port):
393 log.debug("adding peer %s to %s", ip, torrent_id)
394 if not self.torrentmanager[torrent_id].connect_peer(ip, port):
395 log.warning("Error adding peer %s:%s to %s", ip, port, torrent_id)
396
397 @export
398 def move_storage(self, torrent_ids, dest):
399 log.debug("Moving storage %s to %s", torrent_ids, dest)
400 for torrent_id in torrent_ids:
401 if not self.torrentmanager[torrent_id].move_storage(dest):
402 log.warning("Error moving torrent %s to %s", torrent_id, dest)
403
404 @export
405 def pause_all_torrents(self):
406 """Pause all torrents in the session"""
407 for torrent in self.torrentmanager.torrents.values():
408 torrent.pause()
409
410 @export
411 def resume_all_torrents(self):
412 """Resume all torrents in the session"""
413 for torrent in self.torrentmanager.torrents.values():
414 torrent.resume()
415 component.get("EventManager").emit(SessionResumedEvent())
416
417 @export
418 def resume_torrent(self, torrent_ids):
419 log.debug("Resuming: %s", torrent_ids)
420 for torrent_id in torrent_ids:
421 self.torrentmanager[torrent_id].resume()
422
423 def create_torrent_status(self, torrent_id, torrent_keys, plugin_keys, diff=False, update=False, all_keys=False):
424 try:
425 status = self.torrentmanager[torrent_id].get_status(torrent_keys, diff, update=update, all_keys=all_keys)
426 except KeyError:
427 import traceback
428 traceback.print_exc()
429 # Torrent was probaly removed meanwhile
430 return {}
431
432 # Ask the plugin manager to fill in the plugin keys
433 if len(plugin_keys) > 0:
434 status.update(self.pluginmanager.get_status(torrent_id, plugin_keys))
435 return status
436
437 @export
438 def get_torrent_status(self, torrent_id, keys, diff=False):
439 torrent_keys, plugin_keys = self.torrentmanager.separate_keys(keys, [torrent_id])
440 return self.create_torrent_status(torrent_id, torrent_keys, plugin_keys, diff=diff, update=True, all_keys=not keys)
441
442 @export
443 def get_torrents_status(self, filter_dict, keys, diff=False):
444 """
445 returns all torrents , optionally filtered by filter_dict.
446 """
447 torrent_ids = self.filtermanager.filter_torrent_ids(filter_dict)
448 status_dict = {}.fromkeys(torrent_ids)
449 d = self.torrentmanager.torrents_status_update(torrent_ids, keys, diff=diff)
450
451 def add_plugin_fields(args):
452 status_dict, plugin_keys = args
453 # Ask the plugin manager to fill in the plugin keys
454 if len(plugin_keys) > 0:
455 for key in status_dict.keys():
456 status_dict[key].update(self.pluginmanager.get_status(key, plugin_keys))
457 return status_dict
458 d.addCallback(add_plugin_fields)
459 return d
460
461 @export
462 def get_filter_tree(self , show_zero_hits=True, hide_cat=None):
463 """
464 returns {field: [(value,count)] }
465 for use in sidebar(s)
466 """
467 return self.filtermanager.get_filter_tree(show_zero_hits, hide_cat)
468
469 @export
470 def get_session_state(self):
471 """Returns a list of torrent_ids in the session."""
472 # Get the torrent list from the TorrentManager
473 return self.torrentmanager.get_torrent_list()
474
475 @export
476 def get_config(self):
477 """Get all the preferences as a dictionary"""
478 return self.config.config
479
480 @export
481 def get_config_value(self, key):
482 """Get the config value for key"""
483 return self.config.get(key)
484
485 @export
486 def get_config_values(self, keys):
487 """Get the config values for the entered keys"""
488 return dict((key, self.config.get(key)) for key in keys)
489
490 @export
491 def set_config(self, config):
492 """Set the config with values from dictionary"""
493 # Load all the values into the configuration
494 for key in config.keys():
495 if isinstance(config[key], basestring):
496 config[key] = config[key].encode("utf8")
497 self.config[key] = config[key]
498
499 @export
500 def get_listen_port(self):
501 """Returns the active listen port"""
502 return self.session.listen_port()
503
504 @export
505 def get_num_connections(self):
506 """Returns the current number of connections"""
507 return self.session.num_connections()
508
509 @export
510 def get_available_plugins(self):
511 """Returns a list of plugins available in the core"""
512 return self.pluginmanager.get_available_plugins()
513
514 @export
515 def get_enabled_plugins(self):
516 """Returns a list of enabled plugins in the core"""
517 return self.pluginmanager.get_enabled_plugins()
518
519 @export
520 def enable_plugin(self, plugin):
521 self.pluginmanager.enable_plugin(plugin)
522 return None
523
524 @export
525 def disable_plugin(self, plugin):
526 self.pluginmanager.disable_plugin(plugin)
527 return None
528
529 @export
530 def force_recheck(self, torrent_ids):
531 """Forces a data recheck on torrent_ids"""
532 for torrent_id in torrent_ids:
533 self.torrentmanager[torrent_id].force_recheck()
534
535 @export
536 def set_torrent_options(self, torrent_ids, options):
537 """Sets the torrent options for torrent_ids"""
538 for torrent_id in torrent_ids:
539 self.torrentmanager[torrent_id].set_options(options)
540
541 @export
542 def set_torrent_trackers(self, torrent_id, trackers):
543 """Sets a torrents tracker list. trackers will be [{"url", "tier"}]"""
544 return self.torrentmanager[torrent_id].set_trackers(trackers)
545
546 @export
547 def set_torrent_max_connections(self, torrent_id, value):
548 """Sets a torrents max number of connections"""
549 return self.torrentmanager[torrent_id].set_max_connections(value)
550
551 @export
552 def set_torrent_max_upload_slots(self, torrent_id, value):
553 """Sets a torrents max number of upload slots"""
554 return self.torrentmanager[torrent_id].set_max_upload_slots(value)
555
556 @export
557 def set_torrent_max_upload_speed(self, torrent_id, value):
558 """Sets a torrents max upload speed"""
559 return self.torrentmanager[torrent_id].set_max_upload_speed(value)
560
561 @export
562 def set_torrent_max_download_speed(self, torrent_id, value):
563 """Sets a torrents max download speed"""
564 return self.torrentmanager[torrent_id].set_max_download_speed(value)
565
566 @export
567 def set_torrent_file_priorities(self, torrent_id, priorities):
568 """Sets a torrents file priorities"""
569 return self.torrentmanager[torrent_id].set_file_priorities(priorities)
570
571 @export
572 def set_torrent_prioritize_first_last(self, torrent_id, value):
573 """Sets a higher priority to the first and last pieces"""
574 return self.torrentmanager[torrent_id].set_prioritize_first_last(value)
575
576 @export
577 def set_torrent_sequential_download(self, torrent_id, value):
578 """Toggle sequencial pieces download"""
579 return self.torrentmanager[torrent_id].set_sequential_download(value)
580
581 @export
582 def set_torrent_auto_managed(self, torrent_id, value):
583 """Sets the auto managed flag for queueing purposes"""
584 return self.torrentmanager[torrent_id].set_auto_managed(value)
585
586 @export
587 def set_torrent_stop_at_ratio(self, torrent_id, value):
588 """Sets the torrent to stop at 'stop_ratio'"""
589 return self.torrentmanager[torrent_id].set_stop_at_ratio(value)
590
591 @export
592 def set_torrent_stop_ratio(self, torrent_id, value):
593 """Sets the ratio when to stop a torrent if 'stop_at_ratio' is set"""
594 return self.torrentmanager[torrent_id].set_stop_ratio(value)
595
596 @export
597 def set_torrent_remove_at_ratio(self, torrent_id, value):
598 """Sets the torrent to be removed at 'stop_ratio'"""
599 return self.torrentmanager[torrent_id].set_remove_at_ratio(value)
600
601 @export
602 def set_torrent_move_completed(self, torrent_id, value):
603 """Sets the torrent to be moved when completed"""
604 return self.torrentmanager[torrent_id].set_move_completed(value)
605
606 @export
607 def set_torrent_move_completed_path(self, torrent_id, value):
608 """Sets the path for the torrent to be moved when completed"""
609 return self.torrentmanager[torrent_id].set_move_completed_path(value)
610
611 @export(AUTH_LEVEL_ADMIN)
612 def set_torrents_owner(self, torrent_ids, username):
613 """Set's the torrent owner.
614
615 :param torrent_id: the torrent_id of the torrent to remove
616 :type torrent_id: string
617 :param username: the new owner username
618 :type username: string
619
620 :raises DelugeError: if the username is not known
621 """
622 if not self.authmanager.has_account(username):
623 raise DelugeError("Username \"%s\" is not known." % username)
624 if isinstance(torrent_ids, basestring):
625 torrent_ids = [torrent_ids]
626 for torrent_id in torrent_ids:
627 self.torrentmanager[torrent_id].set_owner(username)
628 return None
629
630 @export
631 def set_torrents_shared(self, torrent_ids, shared):
632 if isinstance(torrent_ids, basestring):
633 torrent_ids = [torrent_ids]
634 for torrent_id in torrent_ids:
635 self.torrentmanager[torrent_id].set_options({"shared": shared})
636
637 @export
638 def get_path_size(self, path):
639 """Returns the size of the file or folder 'path' and -1 if the path is
640 unaccessible (non-existent or insufficient privs)"""
641 return deluge.common.get_path_size(path)
642
643 @export
644 def create_torrent(self, path, tracker, piece_length, comment, target,
645 webseeds, private, created_by, trackers, add_to_session):
646
647 log.debug("creating torrent..")
648 threading.Thread(target=self._create_torrent_thread,
649 args=(
650 path,
651 tracker,
652 piece_length,
653 comment,
654 target,
655 webseeds,
656 private,
657 created_by,
658 trackers,
659 add_to_session)).start()
660
661 def _create_torrent_thread(self, path, tracker, piece_length, comment, target,
662 webseeds, private, created_by, trackers, add_to_session):
663 import deluge.metafile
664 deluge.metafile.make_meta_file(
665 path,
666 tracker,
667 piece_length,
668 comment=comment,
669 target=target,
670 webseeds=webseeds,
671 private=private,
672 created_by=created_by,
673 trackers=trackers)
674 log.debug("torrent created!")
675 if add_to_session:
676 options = {}
677 options["download_location"] = os.path.split(path)[0]
678 self.add_torrent_file(os.path.split(target)[1], open(target, "rb").read(), options)
679
680 @export
681 def upload_plugin(self, filename, filedump):
682 """This method is used to upload new plugins to the daemon. It is used
683 when connecting to the daemon remotely and installing a new plugin on
684 the client side. 'plugin_data' is a xmlrpc.Binary object of the file data,
685 ie, plugin_file.read()"""
686
687 try:
688 filedump = base64.decodestring(filedump)
689 except Exception, e:
690 log.error("There was an error decoding the filedump string!")
691 log.exception(e)
692 return
693
694 f = open(os.path.join(deluge.configmanager.get_config_dir(), "plugins", filename), "wb")
695 f.write(filedump)
696 f.close()
697 component.get("CorePluginManager").scan_for_plugins()
698
699 @export
700 def rescan_plugins(self):
701 """
702 Rescans the plugin folders for new plugins
703 """
704 component.get("CorePluginManager").scan_for_plugins()
705
706 @export
707 def rename_files(self, torrent_id, filenames):
708 """
709 Rename files in torrent_id. Since this is an asynchronous operation by
710 libtorrent, watch for the TorrentFileRenamedEvent to know when the
711 files have been renamed.
712
713 :param torrent_id: the torrent_id to rename files
714 :type torrent_id: string
715 :param filenames: a list of index, filename pairs
716 :type filenames: ((index, filename), ...)
717
718 :raises InvalidTorrentError: if torrent_id is invalid
719
720 """
721 if torrent_id not in self.torrentmanager.torrents:
722 raise InvalidTorrentError("torrent_id is not in session")
723
724 self.torrentmanager[torrent_id].rename_files(filenames)
725
726 @export
727 def rename_folder(self, torrent_id, folder, new_folder):
728 """
729 Renames the 'folder' to 'new_folder' in 'torrent_id'. Watch for the
730 TorrentFolderRenamedEvent which is emitted when the folder has been
731 renamed successfully.
732
733 :param torrent_id: the torrent to rename folder in
734 :type torrent_id: string
735 :param folder: the folder to rename
736 :type folder: string
737 :param new_folder: the new folder name
738 :type new_folder: string
739
740 :raises InvalidTorrentError: if the torrent_id is invalid
741
742 """
743 if torrent_id not in self.torrentmanager.torrents:
744 raise InvalidTorrentError("torrent_id is not in session")
745
746 self.torrentmanager[torrent_id].rename_folder(folder, new_folder)
747
748 @export
749 def queue_top(self, torrent_ids):
750 log.debug("Attempting to queue %s to top", torrent_ids)
751 # torrent_ids must be sorted in reverse before moving to preserve order
752 for torrent_id in sorted(torrent_ids, key=self.torrentmanager.get_queue_position, reverse=True):
753 try:
754 # If the queue method returns True, then we should emit a signal
755 if self.torrentmanager.queue_top(torrent_id):
756 component.get("EventManager").emit(TorrentQueueChangedEvent())
757 except KeyError:
758 log.warning("torrent_id: %s does not exist in the queue", torrent_id)
759
760 @export
761 def queue_up(self, torrent_ids):
762 log.debug("Attempting to queue %s to up", torrent_ids)
763 torrents = ((self.torrentmanager.get_queue_position(torrent_id), torrent_id) for torrent_id in torrent_ids)
764 torrent_moved = True
765 prev_queue_position = None
766 #torrent_ids must be sorted before moving.
767 for queue_position, torrent_id in sorted(torrents):
768 # Move the torrent if and only if there is space (by not moving it we preserve the order)
769 if torrent_moved or queue_position - prev_queue_position > 1:
770 try:
771 torrent_moved = self.torrentmanager.queue_up(torrent_id)
772 except KeyError:
773 log.warning("torrent_id: %s does not exist in the queue", torrent_id)
774 # If the torrent moved, then we should emit a signal
775 if torrent_moved:
776 component.get("EventManager").emit(TorrentQueueChangedEvent())
777 else:
778 prev_queue_position = queue_position
779
780 @export
781 def queue_down(self, torrent_ids):
782 log.debug("Attempting to queue %s to down", torrent_ids)
783 torrents = ((self.torrentmanager.get_queue_position(torrent_id), torrent_id) for torrent_id in torrent_ids)
784 torrent_moved = True
785 prev_queue_position = None
786 #torrent_ids must be sorted before moving.
787 for queue_position, torrent_id in sorted(torrents, reverse=True):
788 # Move the torrent if and only if there is space (by not moving it we preserve the order)
789 if torrent_moved or prev_queue_position - queue_position > 1:
790 try:
791 torrent_moved = self.torrentmanager.queue_down(torrent_id)
792 except KeyError:
793 log.warning("torrent_id: %s does not exist in the queue", torrent_id)
794 # If the torrent moved, then we should emit a signal
795 if torrent_moved:
796 component.get("EventManager").emit(TorrentQueueChangedEvent())
797 else:
798 prev_queue_position = queue_position
799
800 @export
801 def queue_bottom(self, torrent_ids):
802 log.debug("Attempting to queue %s to bottom", torrent_ids)
803 # torrent_ids must be sorted before moving to preserve order
804 for torrent_id in sorted(torrent_ids, key=self.torrentmanager.get_queue_position):
805 try:
806 # If the queue method returns True, then we should emit a signal
807 if self.torrentmanager.queue_bottom(torrent_id):
808 component.get("EventManager").emit(TorrentQueueChangedEvent())
809 except KeyError:
810 log.warning("torrent_id: %s does not exist in the queue", torrent_id)
811
812 @export
813 def glob(self, path):
814 return glob.glob(path)
815
816 @export
817 def test_listen_port(self):
818 """
819 Checks if the active port is open
820
821 :returns: True if the port is open, False if not
822 :rtype: bool
823
824 """
825 from twisted.web.client import getPage
826
827 d = getPage("http://deluge-torrent.org/test_port.php?port=%s" %
828 self.get_listen_port(), timeout=30)
829
830 def on_get_page(result):
831 return bool(int(result))
832
833 def logError(failure):
834 log.warning("Error testing listen port: %s", failure)
835
836 d.addCallback(on_get_page)
837 d.addErrback(logError)
838
839 return d
840
841 @export
842 def get_free_space(self, path=None):
843 """
844 Returns the number of free bytes at path
845
846 :param path: the path to check free space at, if None, use the default
847 download location
848 :type path: string
849
850 :returns: the number of free bytes at path
851 :rtype: int
852
853 :raises InvalidPathError: if the path is invalid
854
855 """
856 if not path:
857 path = self.config["download_location"]
858 try:
859 return deluge.common.free_space(path)
860 except InvalidPathError:
861 return 0
862
863 @export
864 def get_libtorrent_version(self):
865 """
866 Returns the libtorrent version.
867
868 :returns: the version
869 :rtype: string
870
871 """
872 return lt.version
873
874 @export(AUTH_LEVEL_ADMIN)
875 def get_known_accounts(self):
876 return self.authmanager.get_known_accounts()
877
878 @export(AUTH_LEVEL_NONE)
879 def get_auth_levels_mappings(self):
880 return (AUTH_LEVELS_MAPPING, AUTH_LEVELS_MAPPING_REVERSE)
881
882 @export(AUTH_LEVEL_ADMIN)
883 def create_account(self, username, password, authlevel):
884 return self.authmanager.create_account(username, password, authlevel)
885
886 @export(AUTH_LEVEL_ADMIN)
887 def update_account(self, username, password, authlevel):
888 return self.authmanager.update_account(username, password, authlevel)
889
890 @export(AUTH_LEVEL_ADMIN)
891 def remove_account(self, username):
892 return self.authmanager.remove_account(username)
Note: See TracBrowser for help on using the repository browser.