mirror of
https://github.com/logos-storage/deluge.git
synced 2026-01-12 01:53:11 +00:00
Compare commits
231 Commits
develop
...
deluge-1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c81fbf788d | ||
|
|
a35b2497f3 | ||
|
|
8fe299dc21 | ||
|
|
e66f0cb503 | ||
|
|
5e129b3c64 | ||
|
|
2d40d2b224 | ||
|
|
ce0234f0ef | ||
|
|
c225eae189 | ||
|
|
afa941df2e | ||
|
|
49cbcf1f9c | ||
|
|
3c3b68e2cc | ||
|
|
b93477c41e | ||
|
|
766c48e3ca | ||
|
|
1f73476dc3 | ||
|
|
5bfb98f9a9 | ||
|
|
d07b53f665 | ||
|
|
449be00e33 | ||
|
|
655af15695 | ||
|
|
a549eac063 | ||
|
|
6fa2728ddc | ||
|
|
9060de9b70 | ||
|
|
57ac902d50 | ||
|
|
05578e0c75 | ||
|
|
a0d4141afd | ||
|
|
493d0ac690 | ||
|
|
fe9fe7977c | ||
|
|
e579a78d26 | ||
|
|
bf96475840 | ||
|
|
93c49495b2 | ||
|
|
54ae8a4482 | ||
|
|
d658c8fe47 | ||
|
|
58134925a2 | ||
|
|
244583ef97 | ||
|
|
0b821640bb | ||
|
|
612ed4123f | ||
|
|
91b9eac075 | ||
|
|
5be93cd5d8 | ||
|
|
816f3ff6d2 | ||
|
|
2e62140d2c | ||
|
|
2381b1ae28 | ||
|
|
6a131f021e | ||
|
|
871d9ac4b0 | ||
|
|
0d665d772d | ||
|
|
aef2a83f25 | ||
|
|
e6cd4d17ee | ||
|
|
b9c49f27fa | ||
|
|
e2118b6bb2 | ||
|
|
d7ba74f01d | ||
|
|
bc9abc8bc9 | ||
|
|
00ad550a52 | ||
|
|
884bfd777e | ||
|
|
e7db1b285f | ||
|
|
ee3a17bf37 | ||
|
|
292ffb35ac | ||
|
|
fd458fbe64 | ||
|
|
b9fdb5a65f | ||
|
|
36f92231a6 | ||
|
|
6cb584d53d | ||
|
|
2eb1a51f6b | ||
|
|
84804d37cc | ||
|
|
1da7a518b5 | ||
|
|
808d9bfba8 | ||
|
|
53b4a06fd1 | ||
|
|
4490cd371a | ||
|
|
7d36a4fa51 | ||
|
|
6f844a86d2 | ||
|
|
03689a805b | ||
|
|
1d0857964e | ||
|
|
fd3a33af03 | ||
|
|
2354eeca7b | ||
|
|
ca0003a7af | ||
|
|
2c615e468b | ||
|
|
3794773e95 | ||
|
|
1731fd641b | ||
|
|
c379b6c3b2 | ||
|
|
e1896d2ace | ||
|
|
5f168f3a25 | ||
|
|
8346b4bb77 | ||
|
|
39bbe76436 | ||
|
|
3bd28208d1 | ||
|
|
ffebfb9cdf | ||
|
|
93091fbe23 | ||
|
|
efd2762255 | ||
|
|
4870d34a52 | ||
|
|
26410ca9c1 | ||
|
|
c1477e45cb | ||
|
|
25e58bc8a2 | ||
|
|
4d2a0b1856 | ||
|
|
82fbbad385 | ||
|
|
29a306e378 | ||
|
|
5cfe3b5601 | ||
|
|
3f458e46dd | ||
|
|
1067eb7f98 | ||
|
|
dca27a4cf9 | ||
|
|
b0ceae8d28 | ||
|
|
dc0bf3bc88 | ||
|
|
3b9d7ff9c3 | ||
|
|
a165d5d746 | ||
|
|
cc02ebea6a | ||
|
|
41ffee5d8a | ||
|
|
14a89b3f8a | ||
|
|
6f0c2af58a | ||
|
|
84cccabf19 | ||
|
|
7fb483adde | ||
|
|
28ce7a70a0 | ||
|
|
14565977fa | ||
|
|
e4420ef354 | ||
|
|
02ad0b93ab | ||
|
|
6d2a001635 | ||
|
|
2a3eb0578c | ||
|
|
60fac28217 | ||
|
|
59e01e7ecf | ||
|
|
4c52ee4229 | ||
|
|
8428524793 | ||
|
|
21c8d02d9a | ||
|
|
0c687c7684 | ||
|
|
78f9efefd9 | ||
|
|
6b228ce31f | ||
|
|
40ce4ec731 | ||
|
|
c029c312e4 | ||
|
|
16c38cd027 | ||
|
|
e23a6b852a | ||
|
|
90e4de54e9 | ||
|
|
c1505bea3a | ||
|
|
6235e832fe | ||
|
|
a71f14c47e | ||
|
|
ed3b23b0fc | ||
|
|
6402634ec1 | ||
|
|
3e68733cfd | ||
|
|
f847a7dc4e | ||
|
|
c7954c20eb | ||
|
|
dc7ed11601 | ||
|
|
d898def9ec | ||
|
|
3e2f6c4060 | ||
|
|
321a22a6f0 | ||
|
|
b4774af2f3 | ||
|
|
d0fd709c74 | ||
|
|
e24212b3f8 | ||
|
|
f8f72af6dc | ||
|
|
b9caa4eeeb | ||
|
|
6c3b216b40 | ||
|
|
eaad867885 | ||
|
|
f6b9f67df8 | ||
|
|
24fe3f7fd5 | ||
|
|
da2fb41a3a | ||
|
|
f8d7f22167 | ||
|
|
b75abc70e5 | ||
|
|
2d821bd79a | ||
|
|
12d9a7a5bd | ||
|
|
c118fa36a9 | ||
|
|
82c91cdc51 | ||
|
|
5501094214 | ||
|
|
b41aa808be | ||
|
|
b9336889f5 | ||
|
|
995f5387eb | ||
|
|
38958d3c4f | ||
|
|
b45e019f08 | ||
|
|
d93fcf6eea | ||
|
|
a2d75a5274 | ||
|
|
8797c3ce1b | ||
|
|
79749cca03 | ||
|
|
5fd8628761 | ||
|
|
0e80b3ea0a | ||
|
|
aa61d33ee2 | ||
|
|
13f29a77dd | ||
|
|
97453d1411 | ||
|
|
62d02091b3 | ||
|
|
161ad0ff0d | ||
|
|
7f323ec0fc | ||
|
|
05aebbb575 | ||
|
|
de85e1dcdc | ||
|
|
1ce480ff23 | ||
|
|
007a9912d2 | ||
|
|
d793b9e6b8 | ||
|
|
72ec926c1a | ||
|
|
0d431ae7db | ||
|
|
f1e0e3be15 | ||
|
|
e8bf5eb592 | ||
|
|
d9a2c4db72 | ||
|
|
8fb7277a82 | ||
|
|
35128cf18f | ||
|
|
6ff1da2391 | ||
|
|
4614188c62 | ||
|
|
80297b8e45 | ||
|
|
9173a9cfdd | ||
|
|
571d1079f6 | ||
|
|
0497c407e1 | ||
|
|
8b58c960f3 | ||
|
|
b615ebe1b8 | ||
|
|
3ed8279219 | ||
|
|
f2944bdeef | ||
|
|
0fcd90ee2c | ||
|
|
26460808e7 | ||
|
|
7aba1af0b2 | ||
|
|
4d2b7df49d | ||
|
|
bd775d0d40 | ||
|
|
7fb3c3c04c | ||
|
|
19c27ee8c5 | ||
|
|
d69b8e1099 | ||
|
|
88daf82cb0 | ||
|
|
99c1a61383 | ||
|
|
2e55769c18 | ||
|
|
259d2633e7 | ||
|
|
8e5aab660c | ||
|
|
fc96e9d02c | ||
|
|
821d403a6c | ||
|
|
5e0d988ef0 | ||
|
|
925ac42f7c | ||
|
|
1ac72b81b6 | ||
|
|
3417caf1d2 | ||
|
|
1bcfc91c35 | ||
|
|
6ee0e5b6be | ||
|
|
58a74202e1 | ||
|
|
a4c6f4e8c9 | ||
|
|
60f3d32de7 | ||
|
|
b3eed8a1f0 | ||
|
|
37137d9b54 | ||
|
|
4fb14b581d | ||
|
|
98da4d0291 | ||
|
|
f0c06f4bc5 | ||
|
|
63d701305c | ||
|
|
99396afa0c | ||
|
|
6231dbd1ca | ||
|
|
8f021c7f06 | ||
|
|
6bb4559d18 | ||
|
|
7c9eea0361 | ||
|
|
15247507d4 | ||
|
|
10de8d5475 | ||
|
|
e304c1f719 | ||
|
|
48d3e89d84 | ||
|
|
44f9e17a09 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "libtorrent"]
|
||||
path = libtorrent
|
||||
url = git://deluge-torrent.org/libtorrent
|
||||
138
ChangeLog
138
ChangeLog
@ -1,4 +1,120 @@
|
||||
=== Deluge 1.3.0 (In Development) ===
|
||||
=== Deluge 1.3.2 (24 May 2011) ===
|
||||
==== Core ====
|
||||
* #1527: Fix Converting unicode to unicode error in move_storage
|
||||
* #1373: Fix creating and moving non-ascii folder names in MS Windows
|
||||
* #1507: Fix temporary file race condition in core/core.py:add_torrent_url
|
||||
* Fix a bug that can occur when upgrading 1.1 config files
|
||||
* #1517: Fix isohunt urls not loading
|
||||
* Handle redirection when adding a torrent by url
|
||||
* #1614: Fix autoadd matching a directory called "torrent"
|
||||
* #1742: Fix failure in Event handler prevents further emissions
|
||||
|
||||
==== GtkUI ====
|
||||
* #1514: Added Indicator Applet
|
||||
* #1494: Add torrent columns Downloaded and Uploaded
|
||||
* #1308: Add torrent column Seeds/Peers ratio
|
||||
* #1646: Add torrent columns for per torrent upload and download speed limits
|
||||
* Add missing icons for Trackers filter
|
||||
* Fix inconsistancies in the text for translation
|
||||
* #1510: Fix cannot create a torrent with only non-zero tier trackers
|
||||
* #1513: Fix unhandled Twisted Error in test_listen_port
|
||||
* #690: Fix renaming folders does not remove old empty folders
|
||||
* #1336: Fix uneeded horizontal scrollbar showing in Files & Peers Tab
|
||||
* #1508: Fix TypeError in cell_data_queue() could not convert argument to correct param type
|
||||
* #1498: Fix double slashes appearing when renaming
|
||||
* #1283: Fix consistent icons for Files tab
|
||||
* #1282: Text for AutoManaged changed to 'On/Off' and localized
|
||||
* Fix Up/Down buttons in Edit Trackers Dialog
|
||||
* Add Key Shortcuts for main menu functions
|
||||
|
||||
==== WebUI ====
|
||||
* #1194: Fix infinite login prompt in web ui through reverse proxy
|
||||
* #1355: Fix slow changing states in webUI
|
||||
* #1536: Fix Edit Trackers window not scrolling and not being resizable
|
||||
* #1799: Fix Missing textbox for "Move completed" in torrent options
|
||||
* #1562: Fix Javascript error in Web UI when re-opening preferences
|
||||
* #1567: Fix js from plugins does not work with different 'base' setting
|
||||
* #1268: Fix torrent errors not displayed in webui
|
||||
* #1323: Fix filter panels not scrollable
|
||||
* Fix file uploads from behind a reverse proxy.
|
||||
* #1333: Fix peer list doesn't update automatically
|
||||
* #1537: Fix editing trackers list, trackers have to be reselected
|
||||
|
||||
==== ConsoleUI ====
|
||||
* #755: Fix can't set listen_ports through console UI
|
||||
* #1500: Fix Console crashes on command longer than terminal width
|
||||
* #1248: Fix deluge-console unicode support on redirected stdout
|
||||
* Fix for deluge-console not adding torrent files on MS Windows
|
||||
* #1450: Fix trailing white space in paths
|
||||
* Misc: Updated help text for deluge-console on MS Windows
|
||||
* #1484: Fix trying to access the screen object when not using interactive mode
|
||||
* #1548: Fix cli argument processing
|
||||
* #1856: Add --sort option to info command
|
||||
* #1857: Add seeding_time, active_time and tracker_status to info command
|
||||
|
||||
==== Scheduler ====
|
||||
* #1506: Fix max speed not restored on a yellow->green transition
|
||||
|
||||
=== Deluge 1.3.1 (31 October 2010) ===
|
||||
==== Core ====
|
||||
* #1369: Fix non-ascii config folders not working in windows
|
||||
|
||||
==== GtkUI ====
|
||||
* #1365: Fix sidebar not updating show/hide trackers
|
||||
* #1247: Fix hang on quit
|
||||
|
||||
==== WebUI ====
|
||||
* #1364: Fix preferences not saving when the web ui plugin is enabled in classic mode
|
||||
* #1377: Fix bug when enabling plugins
|
||||
* #1370: Fix issues with preferences
|
||||
* #1312: Fix deluge-web using 100% CPU
|
||||
|
||||
=== Deluge 1.3.0 (18 September 2010) ===
|
||||
==== Core ====
|
||||
* Fix issue where the save_timer is cancelled when it's not active
|
||||
* Fix unhandled exception when adding a torrent to the session
|
||||
* Moved xdg import so it is not called on Windows, where it is unused. fixes #1343
|
||||
* Fix key error after enabling a plugin that introduces a new status key
|
||||
* Ignore global stop ratio related settings in logic, so per torrent ones are used.
|
||||
* Ensure preferencesmanager only changes intended libtorrent session settings.
|
||||
* Fix issue when adding torrents without a 'session'. This can happen when a plugin adds a torrent, like how the AutoAdd plugin works. The user that adds this torrent will be an empty string.
|
||||
* Add TorrentFileCompleted event
|
||||
|
||||
==== GtkUI ====
|
||||
* Increase max piece size to 8 MiB in create torrent dialog (closes #1358)
|
||||
|
||||
==== Scheduler ====
|
||||
* Add max active downloading and seeding options to scheduler.
|
||||
* Fix scheduler so that it keeps current state, even after global settings change.
|
||||
|
||||
==== AutoAdd ====
|
||||
* AutoAdd plugin can now recover when one of the watchfolders has an unhandled exception.
|
||||
* Fix bug in AutoAdd plugin where watchdirs would not display in gtkui when first enabled.
|
||||
* Fix bugs with unicode torrents in AutoAdd plugin.
|
||||
|
||||
=== Deluge 1.3.0-rc2 (20 August 2010) ===
|
||||
==== Core ====
|
||||
* Fix tracker_icons failing on windows
|
||||
* Fix #1302 an uncaught exception in an state_changed event handler in SessionProxy was preventing the TorrentManager's stop method from properly saving all the resume data
|
||||
* Fix issue with SessionProxy not updating the torrent status correctly when get_torrent_status calls take place within the cache_expiry time
|
||||
|
||||
==== ConsoleUI ====
|
||||
* #1307: Fix not being able to add torrents
|
||||
* #1293: Fix not being able to add paths that contain backslashes
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix uncaught exception when closing deluge in classic mode
|
||||
|
||||
==== Execute ====
|
||||
* #1306: Fix always executing last event
|
||||
|
||||
==== Label ====
|
||||
* Fix being able to remove labels in web ui
|
||||
|
||||
==== WebUI ====
|
||||
* #1319: Fix shift selecting in file trees
|
||||
|
||||
=== Deluge 1.3.0-rc1 (08 May 2010) ===
|
||||
==== Core ====
|
||||
* Implement #1063 option to delete torrent file copy on torrent removal - patch from Ghent
|
||||
* Implement #457 progress bars for folders
|
||||
@ -7,15 +123,33 @@
|
||||
* #1112: Fix renaming files in add torrent dialog
|
||||
* #1247: Fix deluge-gtk from hanging on shutdown
|
||||
* #995: Rewrote tracker_icons
|
||||
* Add AutoAdd plugin
|
||||
* Add Notifications plugin
|
||||
|
||||
==== GtkUI ====
|
||||
* Use new SessionProxy class for caching torrent status client-side
|
||||
* Use torrent status diffs to reduce RPC traffic
|
||||
|
||||
==== Blocklist ====
|
||||
* Implement local blocklist support
|
||||
* #861: Pause transfers until blocklist is imported
|
||||
* Fix redirection not working with relative paths
|
||||
|
||||
==== Execute ====
|
||||
* Fix running commands with the TorrentAdded event
|
||||
* Fix the web interface
|
||||
|
||||
==== Label ====
|
||||
* Fix the web interface (#733)
|
||||
|
||||
==== Web ====
|
||||
* Migrate to ExtJS 3.1
|
||||
* Add gzip compression of HTTP data to the server
|
||||
* Improve the efficiency of the TorrentGrid
|
||||
* Improve the efficiency of the TorrentGrid with lots of torrents (#1026)
|
||||
* Add a base parameter to allow reverse proxying (#1076)
|
||||
* Fix showing all the peers in the details tab (#1054)
|
||||
* Fix uploading torrent files in Opera or IE (#1087)
|
||||
* Complete IE support
|
||||
|
||||
=== Deluge 1.2.0 - "Bursting like an infected kidney" (10 January 2010) ===
|
||||
==== Core ====
|
||||
|
||||
4
DEPENDS
4
DEPENDS
@ -7,6 +7,7 @@
|
||||
* setuptools
|
||||
* gettext
|
||||
* pyxdg
|
||||
* chardet
|
||||
* geoip-database (optional)
|
||||
|
||||
* libtorrent >= 0.14, or build the included version
|
||||
@ -16,9 +17,6 @@
|
||||
* openssl
|
||||
* zlib
|
||||
|
||||
=== UIs ===
|
||||
* chardet
|
||||
|
||||
=== Gtk ===
|
||||
* python-notify (libnotify python wrapper)
|
||||
* pygame
|
||||
|
||||
1
create_potfiles_in.py
Normal file → Executable file
1
create_potfiles_in.py
Normal file → Executable file
@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
|
||||
# Paths to exclude
|
||||
|
||||
@ -41,6 +41,7 @@ import time
|
||||
import subprocess
|
||||
import platform
|
||||
import sys
|
||||
import chardet
|
||||
|
||||
try:
|
||||
import json
|
||||
@ -63,7 +64,6 @@ if not hasattr(json, "dumps"):
|
||||
json.load = load
|
||||
|
||||
import pkg_resources
|
||||
import xdg, xdg.BaseDirectory
|
||||
import gettext
|
||||
import locale
|
||||
|
||||
@ -150,6 +150,7 @@ def get_default_config_dir(filename=None):
|
||||
else:
|
||||
return os.path.join(os.environ.get("APPDATA"), "deluge")
|
||||
else:
|
||||
import xdg.BaseDirectory
|
||||
if filename:
|
||||
return os.path.join(xdg.BaseDirectory.save_config_path("deluge"), filename)
|
||||
else:
|
||||
@ -474,7 +475,7 @@ def free_space(path):
|
||||
sectors, bytes, free, total = map(long, win32file.GetDiskFreeSpace(path))
|
||||
return (free * sectors * bytes)
|
||||
else:
|
||||
disk_data = os.statvfs(path)
|
||||
disk_data = os.statvfs(path.encode("utf8"))
|
||||
block_size = disk_data.f_bsize
|
||||
return disk_data.f_bavail * block_size
|
||||
|
||||
@ -560,6 +561,41 @@ def xml_encode(string):
|
||||
string = string.replace(char, escape)
|
||||
return string
|
||||
|
||||
def decode_string(s, encoding="utf8"):
|
||||
"""
|
||||
Decodes a string and re-encodes it in utf8. If it cannot decode using
|
||||
`:param:encoding` then it will try to detect the string encoding and
|
||||
decode it.
|
||||
|
||||
:param s: string to decode
|
||||
:type s: string
|
||||
:keyword encoding: the encoding to use in the decoding
|
||||
:type encoding: string
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
s = s.decode(encoding).encode("utf8", "ignore")
|
||||
except UnicodeDecodeError:
|
||||
s = s.decode(chardet.detect(s)["encoding"], "ignore").encode("utf8", "ignore")
|
||||
return s
|
||||
|
||||
def utf8_encoded(s):
|
||||
"""
|
||||
Returns a utf8 encoded string of s
|
||||
|
||||
:param s: (unicode) string to (re-)encode
|
||||
:type s: basestring
|
||||
:returns: a utf8 encoded string of s
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
if isinstance(s, str):
|
||||
s = decode_string(s)
|
||||
elif isinstance(s, unicode):
|
||||
s = s.encode("utf8", "ignore")
|
||||
return s
|
||||
|
||||
class VersionSplit(object):
|
||||
"""
|
||||
Used for comparing version numbers.
|
||||
@ -570,13 +606,15 @@ class VersionSplit(object):
|
||||
"""
|
||||
def __init__(self, ver):
|
||||
ver = ver.lower()
|
||||
vs = ver.split("_") if "_" in ver else ver.split("-")
|
||||
vs = ver.replace("_", "-").split("-")
|
||||
self.version = [int(x) for x in vs[0].split(".")]
|
||||
self.suffix = None
|
||||
self.dev = False
|
||||
if len(vs) > 1:
|
||||
for s in ("rc", "alpha", "beta", "dev"):
|
||||
if s in vs[1][:len(s)]:
|
||||
self.suffix = vs[1]
|
||||
if vs[1].startswith(("rc", "alpha", "beta")):
|
||||
self.suffix = vs[1]
|
||||
if vs[-1] == 'dev':
|
||||
self.dev = True
|
||||
|
||||
def __cmp__(self, ver):
|
||||
"""
|
||||
@ -587,19 +625,8 @@ class VersionSplit(object):
|
||||
|
||||
"""
|
||||
|
||||
if self.version > ver.version or (self.suffix and self.suffix[:3] == "dev"):
|
||||
return 1
|
||||
if self.version < ver.version:
|
||||
return -1
|
||||
|
||||
if self.version == ver.version:
|
||||
if self.suffix == ver.suffix:
|
||||
return 0
|
||||
if self.suffix is None:
|
||||
return 1
|
||||
if ver.suffix is None:
|
||||
return -1
|
||||
if self.suffix < ver.suffix:
|
||||
return -1
|
||||
if self.suffix > ver.suffix:
|
||||
return 1
|
||||
# If there is no suffix we use z because we want final
|
||||
# to appear after alpha, beta, and rc alphabetically.
|
||||
v1 = [self.version, self.suffix or 'z', self.dev]
|
||||
v2 = [ver.version, ver.suffix or 'z', ver.dev]
|
||||
return cmp(v1, v2)
|
||||
|
||||
@ -45,9 +45,9 @@ The format of the config file is two json encoded dicts:
|
||||
<version dict>
|
||||
<content dict>
|
||||
|
||||
The version dict contains two keys: file and format. The format version is
|
||||
controlled by the Config class. It should only be changed when anything below
|
||||
it is changed directly by the Config class. An example of this would be if we
|
||||
The version dict contains two keys: file and format. The format version is
|
||||
controlled by the Config class. It should only be changed when anything below
|
||||
it is changed directly by the Config class. An example of this would be if we
|
||||
changed the serializer for the content to something different.
|
||||
|
||||
The config file version is changed by the 'owner' of the config file. This is
|
||||
@ -93,13 +93,13 @@ def prop(func):
|
||||
def find_json_objects(s):
|
||||
"""
|
||||
Find json objects in a string.
|
||||
|
||||
|
||||
:param s: the string to find json objects in
|
||||
:type s: string
|
||||
|
||||
|
||||
:returns: a list of tuples containing start and end locations of json objects in the string `s`
|
||||
:rtype: [(start, end), ...]
|
||||
|
||||
|
||||
"""
|
||||
objects = []
|
||||
opens = 0
|
||||
@ -119,8 +119,8 @@ def find_json_objects(s):
|
||||
start = index + offset + 1
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
|
||||
|
||||
class Config(object):
|
||||
"""
|
||||
This class is used to access/create/modify config files
|
||||
@ -146,7 +146,8 @@ class Config(object):
|
||||
self._save_timer = None
|
||||
|
||||
if defaults:
|
||||
self.__config = dict(defaults)
|
||||
for key, value in defaults.iteritems():
|
||||
self.set_item(key, value)
|
||||
|
||||
# Load the config from file in the config_dir
|
||||
if config_dir:
|
||||
@ -187,6 +188,10 @@ what is currently in the config and it could not convert the value
|
||||
5
|
||||
|
||||
"""
|
||||
if isinstance(value, basestring):
|
||||
value = deluge.common.utf8_encoded(value)
|
||||
|
||||
|
||||
if not self.__config.has_key(key):
|
||||
self.__config[key] = value
|
||||
log.debug("Setting '%s' to %s of %s", key, value, type(value))
|
||||
@ -200,7 +205,10 @@ what is currently in the config and it could not convert the value
|
||||
|
||||
if value is not None and oldtype != type(None) and oldtype != newtype:
|
||||
try:
|
||||
value = oldtype(value)
|
||||
if oldtype == unicode:
|
||||
value = oldtype(value, "utf8")
|
||||
else:
|
||||
value = oldtype(value)
|
||||
except ValueError:
|
||||
log.warning("Type '%s' invalid for '%s'", newtype, key)
|
||||
raise
|
||||
@ -250,7 +258,13 @@ what is currently in the config and it could not convert the value
|
||||
5
|
||||
|
||||
"""
|
||||
return self.__config[key]
|
||||
if isinstance(self.__config[key], str):
|
||||
try:
|
||||
return self.__config[key].decode("utf8")
|
||||
except UnicodeDecodeError:
|
||||
return self.__config[key]
|
||||
else:
|
||||
return self.__config[key]
|
||||
|
||||
def register_change_callback(self, callback):
|
||||
"""
|
||||
@ -348,21 +362,21 @@ what is currently in the config and it could not convert the value
|
||||
return
|
||||
|
||||
objects = find_json_objects(data)
|
||||
|
||||
|
||||
if not len(objects):
|
||||
# No json objects found, try depickling it
|
||||
try:
|
||||
self.__config.update(pickle.loads(data))
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
elif len(objects) == 1:
|
||||
start, end = objects[0]
|
||||
try:
|
||||
self.__config.update(json.loads(data[start:end]))
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
elif len(objects) == 2:
|
||||
try:
|
||||
start, end = objects[0]
|
||||
@ -371,8 +385,8 @@ what is currently in the config and it could not convert the value
|
||||
self.__config.update(json.loads(data[start:end]))
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
|
||||
log.debug("Config %s version: %s.%s loaded: %s", filename,
|
||||
self.__version["format"], self.__version["file"], self.__config)
|
||||
|
||||
@ -396,26 +410,24 @@ what is currently in the config and it could not convert the value
|
||||
version = json.loads(data[start:end])
|
||||
start, end = objects[1]
|
||||
loaded_data = json.loads(data[start:end])
|
||||
|
||||
if self.__config == loaded_data and self.__version == version:
|
||||
# The config has not changed so lets just return
|
||||
self._save_timer.cancel()
|
||||
return
|
||||
except Exception, e:
|
||||
log.warning("Unable to open config file: %s", filename)
|
||||
|
||||
|
||||
if self._save_timer and self._save_timer.active():
|
||||
self._save_timer.cancel()
|
||||
return True
|
||||
except IOError, e:
|
||||
log.warning("Unable to open config file: %s because: %s", filename, e)
|
||||
|
||||
# Save the new config and make sure it's written to disk
|
||||
try:
|
||||
log.debug("Saving new config file %s", filename + ".new")
|
||||
f = open(filename + ".new", "wb")
|
||||
json.dump(self.__version, f, indent=2)
|
||||
json.dump(self.__version, f, indent=2)
|
||||
json.dump(self.__config, f, indent=2)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
f.close()
|
||||
except Exception, e:
|
||||
except IOError, e:
|
||||
log.error("Error writing new config file: %s", e)
|
||||
return False
|
||||
|
||||
|
||||
@ -74,12 +74,12 @@ class AutoAdd(component.Component):
|
||||
return
|
||||
|
||||
for filename in os.listdir(self.config["autoadd_location"]):
|
||||
if filename.split(".")[-1] == "torrent":
|
||||
try:
|
||||
filepath = os.path.join(self.config["autoadd_location"], filename)
|
||||
except UnicodeDecodeError, e:
|
||||
log.error("Unable to auto add torrent due to inproper filename encoding: %s", e)
|
||||
continue
|
||||
try:
|
||||
filepath = os.path.join(self.config["autoadd_location"], filename)
|
||||
except UnicodeDecodeError, e:
|
||||
log.error("Unable to auto add torrent due to improper filename encoding: %s", e)
|
||||
continue
|
||||
if os.path.isfile(filepath) and filename.endswith(".torrent"):
|
||||
try:
|
||||
filedump = self.load_torrent(filepath)
|
||||
except (RuntimeError, Exception), e:
|
||||
|
||||
@ -42,11 +42,14 @@ import shutil
|
||||
import threading
|
||||
import pkg_resources
|
||||
import warnings
|
||||
import tempfile
|
||||
from urlparse import urljoin
|
||||
|
||||
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.internet.task import LoopingCall
|
||||
import twisted.web.client
|
||||
import twisted.web.error
|
||||
|
||||
from deluge.httpdownloader import download_file
|
||||
from deluge.log import LOG as log
|
||||
@ -236,20 +239,35 @@ class Core(component.Component):
|
||||
:returns: a Deferred which returns the torrent_id as a str or None
|
||||
"""
|
||||
log.info("Attempting to add url %s", url)
|
||||
def on_get_file(filename):
|
||||
def on_download_success(filename):
|
||||
# We got the file, so add it to the session
|
||||
data = open(filename, "rb").read()
|
||||
f = open(filename, "rb")
|
||||
data = f.read()
|
||||
f.close()
|
||||
try:
|
||||
os.remove(filename)
|
||||
except Exception, e:
|
||||
log.warning("Couldn't remove temp file: %s", e)
|
||||
return self.add_torrent_file(filename, base64.encodestring(data), options)
|
||||
|
||||
def on_get_file_error(failure):
|
||||
# Log the error and pass the failure onto the client
|
||||
log.error("Error occured downloading torrent from %s", url)
|
||||
log.error("Reason: %s", failure.getErrorMessage())
|
||||
return failure
|
||||
def on_download_fail(failure):
|
||||
if failure.check(twisted.web.error.PageRedirect):
|
||||
new_url = urljoin(url, failure.getErrorMessage().split(" to ")[1])
|
||||
result = download_file(new_url, tempfile.mkstemp()[1], headers=headers, force_filename=True)
|
||||
result.addCallbacks(on_download_success, on_download_fail)
|
||||
elif failure.check(twisted.web.client.PartialDownloadError):
|
||||
result = download_file(url, tempfile.mkstemp()[1], headers=headers, force_filename=True,
|
||||
allow_compression=False)
|
||||
result.addCallbacks(on_download_success, on_download_fail)
|
||||
else:
|
||||
# Log the error and pass the failure onto the client
|
||||
log.error("Error occured downloading torrent from %s", url)
|
||||
log.error("Reason: %s", failure.getErrorMessage())
|
||||
result = failure
|
||||
return result
|
||||
|
||||
d = download_file(url, url.split("/")[-1], headers=headers)
|
||||
d.addCallback(on_get_file)
|
||||
d.addErrback(on_get_file_error)
|
||||
d = download_file(url, tempfile.mkstemp()[1], headers=headers, force_filename=True)
|
||||
d.addCallbacks(on_download_success, on_download_fail)
|
||||
return d
|
||||
|
||||
@export
|
||||
@ -747,7 +765,11 @@ class Core(component.Component):
|
||||
def on_get_page(result):
|
||||
return bool(int(result))
|
||||
|
||||
def logError(failure):
|
||||
log.warning("Error testing listen port: %s", failure)
|
||||
|
||||
d.addCallback(on_get_page)
|
||||
d.addErrback(logError)
|
||||
|
||||
return d
|
||||
|
||||
@ -768,7 +790,10 @@ class Core(component.Component):
|
||||
"""
|
||||
if not path:
|
||||
path = self.config["download_location"]
|
||||
return deluge.common.free_space(path)
|
||||
try:
|
||||
return deluge.common.free_space(path)
|
||||
except InvalidPathError:
|
||||
return 0
|
||||
|
||||
@export
|
||||
def get_libtorrent_version(self):
|
||||
|
||||
@ -53,7 +53,10 @@ class EventManager(component.Component):
|
||||
if event.name in self.handlers:
|
||||
for handler in self.handlers[event.name]:
|
||||
#log.debug("Running handler %s for event %s with args: %s", event.name, handler, event.args)
|
||||
handler(*event.args)
|
||||
try:
|
||||
handler(*event.args)
|
||||
except:
|
||||
log.error("Event handler %s failed in %s", event.name, handler)
|
||||
|
||||
def register_event_handler(self, event, handler):
|
||||
"""
|
||||
|
||||
@ -196,9 +196,8 @@ class FilterManager(component.Component):
|
||||
value = status[field]
|
||||
items[field][value] = items[field].get(value, 0) + 1
|
||||
|
||||
items["tracker_host"]["All"] = len(torrent_ids)
|
||||
|
||||
if "tracker_host" in items:
|
||||
items["tracker_host"]["All"] = len(torrent_ids)
|
||||
items["tracker_host"]["Error"] = len(tracker_error_filter(torrent_ids, ("Error",)))
|
||||
|
||||
if "state" in tree_keys and not show_zero_hits:
|
||||
|
||||
@ -152,7 +152,6 @@ class PreferencesManager(component.Component):
|
||||
def start(self):
|
||||
self.core = component.get("Core")
|
||||
self.session = component.get("Core").session
|
||||
self.settings = component.get("Core").settings
|
||||
|
||||
# Register set functions in the Config
|
||||
self.config.register_set_function("torrentfiles_location",
|
||||
@ -233,6 +232,11 @@ class PreferencesManager(component.Component):
|
||||
self.new_release_timer.stop()
|
||||
|
||||
# Config set functions
|
||||
def session_set_setting(self, key, value):
|
||||
settings = self.session.settings()
|
||||
setattr(settings, key, value)
|
||||
self.session.set_settings(settings)
|
||||
|
||||
def _on_config_value_change(self, key, value):
|
||||
component.get("EventManager").emit(ConfigValueChangedEvent(key, value))
|
||||
|
||||
@ -274,8 +278,7 @@ class PreferencesManager(component.Component):
|
||||
def _on_set_outgoing_ports(self, key, value):
|
||||
if not self.config["random_outgoing_ports"]:
|
||||
log.debug("outgoing port range set to %s-%s", value[0], value[1])
|
||||
self.settings.outgoing_ports = value[0], value[1]
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("outgoing_ports", (value[0], value[1]))
|
||||
|
||||
def _on_set_random_outgoing_ports(self, key, value):
|
||||
if value:
|
||||
@ -284,13 +287,11 @@ class PreferencesManager(component.Component):
|
||||
def _on_set_peer_tos(self, key, value):
|
||||
log.debug("setting peer_tos to: %s", value)
|
||||
try:
|
||||
self.settings.peer_tos = chr(int(value, 16))
|
||||
self.session_set_setting("peer_tos", chr(int(value, 16)))
|
||||
except ValueError, e:
|
||||
log.debug("Invalid tos byte: %s", e)
|
||||
return
|
||||
|
||||
self.session.set_settings(self.settings)
|
||||
|
||||
def _on_set_dht(self, key, value):
|
||||
log.debug("dht value set to %s", value)
|
||||
state_file = deluge.configmanager.get_config_dir("dht.state")
|
||||
@ -387,51 +388,39 @@ class PreferencesManager(component.Component):
|
||||
self.session.set_max_half_open_connections(value)
|
||||
|
||||
def _on_set_max_connections_per_second(self, key, value):
|
||||
self.settings.connection_speed = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("connection_speed", value)
|
||||
|
||||
def _on_ignore_limits_on_local_network(self, key, value):
|
||||
self.settings.ignore_limits_on_local_network = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("ignore_limits_on_local_network", value)
|
||||
|
||||
def _on_set_share_ratio_limit(self, key, value):
|
||||
log.debug("%s set to %s..", key, value)
|
||||
self.settings.share_ratio_limit = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("share_ratio_limit", value)
|
||||
|
||||
def _on_set_seed_time_ratio_limit(self, key, value):
|
||||
log.debug("%s set to %s..", key, value)
|
||||
self.settings.seed_time_ratio_limit = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("seed_time_ratio_limit", value)
|
||||
|
||||
def _on_set_seed_time_limit(self, key, value):
|
||||
log.debug("%s set to %s..", key, value)
|
||||
# This value is stored in minutes in deluge, but libtorrent wants seconds
|
||||
self.settings.seed_time_limit = int(value * 60)
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("seed_time_limit", int(value * 60))
|
||||
|
||||
def _on_set_max_active_downloading(self, key, value):
|
||||
log.debug("%s set to %s..", key, value)
|
||||
log.debug("active_downloads: %s", self.settings.active_downloads)
|
||||
self.settings.active_downloads = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("active_downloads", value)
|
||||
|
||||
def _on_set_max_active_seeding(self, key, value):
|
||||
log.debug("%s set to %s..", key, value)
|
||||
log.debug("active_seeds: %s", self.settings.active_seeds)
|
||||
self.settings.active_seeds = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("active_seeds", value)
|
||||
|
||||
def _on_set_max_active_limit(self, key, value):
|
||||
log.debug("%s set to %s..", key, value)
|
||||
log.debug("active_limit: %s", self.settings.active_limit)
|
||||
self.settings.active_limit = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("active_limit", value)
|
||||
|
||||
def _on_set_dont_count_slow_torrents(self, key, value):
|
||||
log.debug("%s set to %s..", key, value)
|
||||
self.settings.dont_count_slow_torrents = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("dont_count_slow_torrents", value)
|
||||
|
||||
def _on_send_info(self, key, value):
|
||||
log.debug("Sending anonymous stats..")
|
||||
@ -491,8 +480,7 @@ class PreferencesManager(component.Component):
|
||||
|
||||
def _on_rate_limit_ip_overhead(self, key, value):
|
||||
log.debug("%s: %s", key, value)
|
||||
self.settings.rate_limit_ip_overhead = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("rate_limit_ip_overhead", value)
|
||||
|
||||
def _on_geoip_db_location(self, key, value):
|
||||
log.debug("%s: %s", key, value)
|
||||
@ -514,10 +502,8 @@ class PreferencesManager(component.Component):
|
||||
|
||||
def _on_cache_size(self, key, value):
|
||||
log.debug("%s: %s", key, value)
|
||||
self.settings.cache_size = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("cache_size", value)
|
||||
|
||||
def _on_cache_expiry(self, key, value):
|
||||
log.debug("%s: %s", key, value)
|
||||
self.settings.cache_expiry = value
|
||||
self.session.set_settings(self.settings)
|
||||
self.session_set_setting("cache_expiry", value)
|
||||
|
||||
@ -47,7 +47,11 @@ from twisted.internet import ssl, reactor, defer
|
||||
from OpenSSL import crypto, SSL
|
||||
from types import FunctionType
|
||||
|
||||
import deluge.rencode as rencode
|
||||
try:
|
||||
import rencode
|
||||
except ImportError:
|
||||
import deluge.rencode as rencode
|
||||
|
||||
from deluge.log import LOG as log
|
||||
|
||||
import deluge.component as component
|
||||
@ -90,13 +94,13 @@ def export(auth_level=AUTH_LEVEL_DEFAULT):
|
||||
def format_request(call):
|
||||
"""
|
||||
Format the RPCRequest message for debug printing
|
||||
|
||||
|
||||
:param call: the request
|
||||
:type call: a RPCRequest
|
||||
|
||||
|
||||
:returns: a formatted string for printing
|
||||
:rtype: str
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
s = call[1] + "("
|
||||
@ -111,7 +115,7 @@ def format_request(call):
|
||||
return "UnicodeEncodeError, call: %s" % call
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
class DelugeError(Exception):
|
||||
pass
|
||||
|
||||
@ -139,7 +143,7 @@ class DelugeRPCProtocol(Protocol):
|
||||
"""
|
||||
This method is called whenever data is received from a client. The
|
||||
only message that a client sends to the server is a RPC Request message.
|
||||
If the RPC Request message is valid, then the method is called in
|
||||
If the RPC Request message is valid, then the method is called in
|
||||
:meth:`dispatch`.
|
||||
|
||||
:param data: the data from the client. It should be a zlib compressed
|
||||
@ -187,7 +191,7 @@ class DelugeRPCProtocol(Protocol):
|
||||
:param data: the object that is to be sent to the client. This should
|
||||
be one of the RPC message types.
|
||||
:type data: object
|
||||
|
||||
|
||||
"""
|
||||
self.transport.write(zlib.compress(rencode.dumps(data)))
|
||||
|
||||
@ -254,7 +258,7 @@ class DelugeRPCProtocol(Protocol):
|
||||
try:
|
||||
ret = component.get("AuthManager").authorize(*args, **kwargs)
|
||||
if ret:
|
||||
self.factory.authorized_sessions[self.transport.sessionno] = ret
|
||||
self.factory.authorized_sessions[self.transport.sessionno] = (ret, args[0])
|
||||
self.factory.session_protocols[self.transport.sessionno] = self
|
||||
except Exception, e:
|
||||
sendError()
|
||||
@ -283,7 +287,7 @@ class DelugeRPCProtocol(Protocol):
|
||||
if method in self.factory.methods and self.transport.sessionno in self.factory.authorized_sessions:
|
||||
try:
|
||||
method_auth_requirement = self.factory.methods[method]._rpcserver_auth_level
|
||||
auth_level = self.factory.authorized_sessions[self.transport.sessionno]
|
||||
auth_level = self.factory.authorized_sessions[self.transport.sessionno][0]
|
||||
if auth_level < method_auth_requirement:
|
||||
# This session is not allowed to call this method
|
||||
log.debug("Session %s is trying to call a method it is not authorized to call!", self.transport.sessionno)
|
||||
@ -338,7 +342,7 @@ class RPCServer(component.Component):
|
||||
self.factory = Factory()
|
||||
self.factory.protocol = DelugeRPCProtocol
|
||||
self.factory.session_id = -1
|
||||
|
||||
|
||||
# Holds the registered methods
|
||||
self.factory.methods = {}
|
||||
# Holds the session_ids and auth levels
|
||||
@ -417,26 +421,41 @@ class RPCServer(component.Component):
|
||||
def get_session_id(self):
|
||||
"""
|
||||
Returns the session id of the current RPC.
|
||||
|
||||
|
||||
:returns: the session id, this will be -1 if no connections have been made
|
||||
:rtype: int
|
||||
|
||||
"""
|
||||
return self.factory.session_id
|
||||
|
||||
|
||||
def get_session_user(self):
|
||||
"""
|
||||
Returns the username calling the current RPC.
|
||||
|
||||
:returns: the username of the user calling the current RPC
|
||||
:rtype: string
|
||||
|
||||
"""
|
||||
session_id = self.get_session_id()
|
||||
if session_id > -1 and session_id in self.factory.authorized_sessions:
|
||||
return self.factory.authorized_sessions[session_id][1]
|
||||
else:
|
||||
# No connections made yet
|
||||
return ""
|
||||
|
||||
def is_session_valid(self, session_id):
|
||||
"""
|
||||
Checks if the session is still valid, eg, if the client is still connected.
|
||||
|
||||
|
||||
:param session_id: the session id
|
||||
:type session_id: int
|
||||
|
||||
|
||||
:returns: True if the session is valid
|
||||
:rtype: bool
|
||||
|
||||
|
||||
"""
|
||||
return session_id in self.factory.authorized_sessions
|
||||
|
||||
|
||||
def emit_event(self, event):
|
||||
"""
|
||||
Emits the event to interested clients.
|
||||
|
||||
@ -82,7 +82,7 @@ class Torrent(object):
|
||||
self.config = ConfigManager("core.conf")
|
||||
|
||||
self.rpcserver = component.get("RPCServer")
|
||||
|
||||
|
||||
# This dict holds previous status dicts returned for this torrent
|
||||
# We use this to return dicts that only contain changes from the previous
|
||||
# {session_id: status_dict, ...}
|
||||
@ -90,7 +90,7 @@ class Torrent(object):
|
||||
from twisted.internet.task import LoopingCall
|
||||
self.prev_status_cleanup_loop = LoopingCall(self.cleanup_prev_status)
|
||||
self.prev_status_cleanup_loop.start(10)
|
||||
|
||||
|
||||
# Set the libtorrent handle
|
||||
self.handle = handle
|
||||
# Set the torrent_id for this torrent
|
||||
@ -179,6 +179,11 @@ class Torrent(object):
|
||||
else:
|
||||
self.time_added = time.time()
|
||||
|
||||
# Keep track if we're forcing a recheck of the torrent so that we can
|
||||
# repause it after its done if necessary
|
||||
self.forcing_recheck = False
|
||||
self.forcing_recheck_paused = False
|
||||
|
||||
log.debug("Torrent object created.")
|
||||
|
||||
## Options methods ##
|
||||
@ -393,12 +398,11 @@ class Torrent(object):
|
||||
else:
|
||||
status = self.status
|
||||
|
||||
if self.is_finished and (self.options["stop_at_ratio"] or self.config["stop_seed_at_ratio"]):
|
||||
if self.is_finished and self.options["stop_at_ratio"]:
|
||||
# We're a seed, so calculate the time to the 'stop_share_ratio'
|
||||
if not status.upload_payload_rate:
|
||||
return 0
|
||||
stop_ratio = self.config["stop_seed_ratio"] if self.config["stop_seed_at_ratio"] else self.options["stop_ratio"]
|
||||
|
||||
stop_ratio = self.options["stop_ratio"]
|
||||
return ((status.all_time_download * stop_ratio) - status.all_time_upload) / status.upload_payload_rate
|
||||
|
||||
left = status.total_wanted - status.total_done
|
||||
@ -543,18 +547,18 @@ class Torrent(object):
|
||||
def get_status(self, keys, diff=False):
|
||||
"""
|
||||
Returns the status of the torrent based on the keys provided
|
||||
|
||||
|
||||
:param keys: the keys to get the status on
|
||||
:type keys: list of str
|
||||
:param diff: if True, will return a diff of the changes since the last
|
||||
call to get_status based on the session_id
|
||||
:type diff: bool
|
||||
|
||||
|
||||
:returns: a dictionary of the status keys and their values
|
||||
:rtype: dict
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# Create the full dictionary
|
||||
self.status = self.handle.status()
|
||||
if self.handle.has_metadata():
|
||||
@ -568,6 +572,13 @@ class Torrent(object):
|
||||
if distributed_copies < 0:
|
||||
distributed_copies = 0.0
|
||||
|
||||
# Calculate the seeds:peers ratio
|
||||
if self.status.num_incomplete == 0:
|
||||
# Use -1.0 to signify infinity
|
||||
seeds_peers_ratio = -1.0
|
||||
else:
|
||||
seeds_peers_ratio = self.status.num_complete / float(self.status.num_incomplete)
|
||||
|
||||
#if you add a key here->add it to core.py STATUS_KEYS too.
|
||||
full_status = {
|
||||
"active_time": self.status.active_time,
|
||||
@ -586,6 +597,8 @@ class Torrent(object):
|
||||
"message": self.statusmsg,
|
||||
"move_on_completed_path": self.options["move_completed_path"],
|
||||
"move_on_completed": self.options["move_completed"],
|
||||
"move_completed_path": self.options["move_completed_path"],
|
||||
"move_completed": self.options["move_completed"],
|
||||
"next_announce": self.status.next_announce.seconds,
|
||||
"num_peers": self.status.num_peers - self.status.num_seeds,
|
||||
"num_seeds": self.status.num_seeds,
|
||||
@ -595,6 +608,7 @@ class Torrent(object):
|
||||
"remove_at_ratio": self.options["remove_at_ratio"],
|
||||
"save_path": self.options["download_location"],
|
||||
"seeding_time": self.status.seeding_time,
|
||||
"seeds_peers_ratio": seeds_peers_ratio,
|
||||
"seed_rank": self.status.seed_rank,
|
||||
"state": self.state,
|
||||
"stop_at_ratio": self.options["stop_at_ratio"],
|
||||
@ -699,7 +713,7 @@ class Torrent(object):
|
||||
status_dict[key] = full_status[key]
|
||||
elif key in fns:
|
||||
status_dict[key] = fns[key]()
|
||||
|
||||
|
||||
session_id = self.rpcserver.get_session_id()
|
||||
if diff:
|
||||
if session_id in self.prev_status:
|
||||
@ -711,7 +725,7 @@ class Torrent(object):
|
||||
status_diff[key] = value
|
||||
else:
|
||||
status_diff[key] = value
|
||||
|
||||
|
||||
self.prev_status[session_id] = status_dict
|
||||
return status_diff
|
||||
|
||||
@ -761,13 +775,8 @@ class Torrent(object):
|
||||
|
||||
if self.handle.is_finished():
|
||||
# If the torrent has already reached it's 'stop_seed_ratio' then do not do anything
|
||||
if self.config["stop_seed_at_ratio"] or self.options["stop_at_ratio"]:
|
||||
if self.options["stop_at_ratio"]:
|
||||
ratio = self.options["stop_ratio"]
|
||||
else:
|
||||
ratio = self.config["stop_seed_ratio"]
|
||||
|
||||
if self.get_ratio() >= ratio:
|
||||
if self.options["stop_at_ratio"]:
|
||||
if self.get_ratio() >= self.options["stop_ratio"]:
|
||||
#XXX: This should just be returned in the RPC Response, no event
|
||||
#self.signals.emit_event("torrent_resume_at_stop_ratio")
|
||||
return
|
||||
@ -794,8 +803,25 @@ class Torrent(object):
|
||||
|
||||
def move_storage(self, dest):
|
||||
"""Move a torrent's storage location"""
|
||||
|
||||
# Attempt to convert utf8 path to unicode
|
||||
# Note: Inconsistent encoding for 'dest', needs future investigation
|
||||
try:
|
||||
self.handle.move_storage(dest.encode("utf8"))
|
||||
dest_u = unicode(dest, "utf-8")
|
||||
except TypeError:
|
||||
# String is already unicode
|
||||
dest_u = dest
|
||||
|
||||
if not os.path.exists(dest_u):
|
||||
try:
|
||||
# Try to make the destination path if it doesn't exist
|
||||
os.makedirs(dest_u)
|
||||
except IOError, e:
|
||||
log.exception(e)
|
||||
log.error("Could not move storage for torrent %s since %s does not exist and could not create the directory.", self.torrent_id, dest_u)
|
||||
return False
|
||||
try:
|
||||
self.handle.move_storage(dest_u)
|
||||
except:
|
||||
return False
|
||||
|
||||
@ -857,12 +883,15 @@ class Torrent(object):
|
||||
|
||||
def force_recheck(self):
|
||||
"""Forces a recheck of the torrents pieces"""
|
||||
paused = self.handle.is_paused()
|
||||
try:
|
||||
self.handle.force_recheck()
|
||||
self.handle.resume()
|
||||
except Exception, e:
|
||||
log.debug("Unable to force recheck: %s", e)
|
||||
return False
|
||||
self.forcing_recheck = True
|
||||
self.forcing_recheck_paused = paused
|
||||
return True
|
||||
|
||||
def rename_files(self, filenames):
|
||||
@ -879,8 +908,8 @@ class Torrent(object):
|
||||
log.error("Attempting to rename a folder with an invalid folder name: %s", new_folder)
|
||||
return
|
||||
|
||||
if new_folder[-1:] != "/":
|
||||
new_folder += "/"
|
||||
# Make sure the new folder path is nice and has a trailing slash
|
||||
new_folder = os.path.normpath(new_folder) + "/"
|
||||
|
||||
wait_on_folder = (folder, new_folder, [])
|
||||
for f in self.get_files():
|
||||
@ -889,12 +918,12 @@ class Torrent(object):
|
||||
wait_on_folder[2].append(f["index"])
|
||||
self.handle.rename_file(f["index"], f["path"].replace(folder, new_folder, 1).encode("utf-8"))
|
||||
self.waiting_on_folder_rename.append(wait_on_folder)
|
||||
|
||||
|
||||
def cleanup_prev_status(self):
|
||||
"""
|
||||
This method gets called to check the validity of the keys in the prev_status
|
||||
dict. If the key is no longer valid, the dict will be deleted.
|
||||
|
||||
|
||||
"""
|
||||
for key in self.prev_status.keys():
|
||||
if not self.rpcserver.is_session_valid(key):
|
||||
|
||||
@ -41,22 +41,21 @@ import os
|
||||
import time
|
||||
import shutil
|
||||
import operator
|
||||
import re
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet.task import LoopingCall
|
||||
|
||||
from deluge._libtorrent import lt
|
||||
|
||||
|
||||
from deluge.event import *
|
||||
from deluge.error import *
|
||||
import deluge.common
|
||||
import deluge.component as component
|
||||
from deluge.configmanager import ConfigManager, get_config_dir
|
||||
from deluge.core.torrent import Torrent
|
||||
from deluge.core.torrent import TorrentOptions
|
||||
import deluge.core.oldstateupgrader
|
||||
from deluge.ui.common import utf8_encoded
|
||||
from deluge.common import utf8_encoded
|
||||
|
||||
from deluge.log import LOG as log
|
||||
|
||||
@ -192,6 +191,8 @@ class TorrentManager(component.Component):
|
||||
self.on_alert_metadata_received)
|
||||
self.alerts.register_handler("file_error_alert",
|
||||
self.on_alert_file_error)
|
||||
self.alerts.register_handler("file_completed_alert",
|
||||
self.on_alert_file_completed)
|
||||
|
||||
def start(self):
|
||||
# Get the pluginmanager reference
|
||||
@ -256,16 +257,13 @@ class TorrentManager(component.Component):
|
||||
|
||||
def update(self):
|
||||
for torrent_id, torrent in self.torrents.items():
|
||||
if self.config["stop_seed_at_ratio"] or torrent.options["stop_at_ratio"] and torrent.state not in ("Checking", "Allocating", "Paused", "Queued"):
|
||||
if torrent.options["stop_at_ratio"] and torrent.state not in ("Checking", "Allocating", "Paused", "Queued"):
|
||||
# If the global setting is set, but the per-torrent isn't.. Just skip to the next torrent
|
||||
# This is so that a user can turn-off the stop at ratio option on a per-torrent basis
|
||||
if self.config["stop_seed_at_ratio"] and not torrent.options["stop_at_ratio"]:
|
||||
if not torrent.options["stop_at_ratio"]:
|
||||
continue
|
||||
stop_ratio = self.config["stop_seed_ratio"]
|
||||
if torrent.options["stop_at_ratio"]:
|
||||
stop_ratio = torrent.options["stop_ratio"]
|
||||
if torrent.get_ratio() >= stop_ratio and torrent.is_finished:
|
||||
if self.config["remove_seed_at_ratio"] or torrent.options["remove_at_ratio"]:
|
||||
if torrent.get_ratio() >= torrent.options["stop_ratio"] and torrent.is_finished:
|
||||
if torrent.options["remove_at_ratio"]:
|
||||
self.remove(torrent_id)
|
||||
break
|
||||
if not torrent.handle.is_paused():
|
||||
@ -476,6 +474,7 @@ class TorrentManager(component.Component):
|
||||
# Emit the torrent_added signal
|
||||
component.get("EventManager").emit(TorrentAddedEvent(torrent.torrent_id))
|
||||
|
||||
log.info("Torrent %s added by user: %s", torrent.get_status(["name"])["name"], component.get("RPCServer").get_session_user())
|
||||
return torrent.torrent_id
|
||||
|
||||
def load_torrent(self, torrent_id):
|
||||
@ -515,6 +514,8 @@ class TorrentManager(component.Component):
|
||||
if torrent_id not in self.torrents:
|
||||
raise InvalidTorrentError("torrent_id not in session")
|
||||
|
||||
torrent_name = self.torrents[torrent_id].get_status(["name"])["name"]
|
||||
|
||||
# Emit the signal to the clients
|
||||
component.get("EventManager").emit(PreTorrentRemovedEvent(torrent_id))
|
||||
|
||||
@ -562,7 +563,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
# Emit the signal to the clients
|
||||
component.get("EventManager").emit(TorrentRemovedEvent(torrent_id))
|
||||
|
||||
log.info("Torrent %s removed by user: %s", torrent_name, component.get("RPCServer").get_session_user())
|
||||
return True
|
||||
|
||||
def load_state(self):
|
||||
@ -731,6 +732,38 @@ class TorrentManager(component.Component):
|
||||
except IOError:
|
||||
log.warning("Error trying to save fastresume file")
|
||||
|
||||
def remove_empty_folders(self, torrent_id, folder):
|
||||
"""
|
||||
Recursively removes folders but only if they are empty.
|
||||
Cleans up after libtorrent folder renames.
|
||||
|
||||
"""
|
||||
if torrent_id not in self.torrents:
|
||||
raise InvalidTorrentError("torrent_id is not in session")
|
||||
|
||||
info = self.torrents[torrent_id].get_status(['save_path'])
|
||||
# Regex removes leading slashes that causes join function to ignore save_path
|
||||
folder_full_path = os.path.join(info['save_path'], re.sub("^/*", "", folder))
|
||||
folder_full_path = os.path.normpath(folder_full_path)
|
||||
|
||||
try:
|
||||
if not os.listdir(folder_full_path):
|
||||
os.removedirs(folder_full_path)
|
||||
log.debug("Removed Empty Folder %s", folder_full_path)
|
||||
else:
|
||||
for root, dirs, files in os.walk(folder_full_path, topdown=False):
|
||||
for name in dirs:
|
||||
try:
|
||||
os.removedirs(os.path.join(root, name))
|
||||
log.debug("Removed Empty Folder %s", os.path.join(root, name))
|
||||
except OSError as (errno, strerror):
|
||||
if errno == 39:
|
||||
# Error raised if folder is not empty
|
||||
log.debug("%s", strerror)
|
||||
|
||||
except OSError as (errno, strerror):
|
||||
log.debug("Cannot Remove Folder: %s (ErrNo %s)", strerror, errno)
|
||||
|
||||
def queue_top(self, torrent_id):
|
||||
"""Queue torrent to top"""
|
||||
if self.torrents[torrent_id].get_queue_position() == 0:
|
||||
@ -850,6 +883,13 @@ class TorrentManager(component.Component):
|
||||
except:
|
||||
return
|
||||
|
||||
# Check to see if we're forcing a recheck and set it back to paused
|
||||
# if necessary
|
||||
if torrent.forcing_recheck:
|
||||
torrent.forcing_recheck = False
|
||||
if torrent.forcing_recheck_paused:
|
||||
torrent.handle.pause()
|
||||
|
||||
# Set the torrent state
|
||||
torrent.update_state()
|
||||
|
||||
@ -985,6 +1025,8 @@ class TorrentManager(component.Component):
|
||||
if len(wait_on_folder[2]) == 1:
|
||||
# This is the last alert we were waiting for, time to send signal
|
||||
component.get("EventManager").emit(TorrentFolderRenamedEvent(torrent_id, wait_on_folder[0], wait_on_folder[1]))
|
||||
# Empty folders are removed after libtorrent folder renames
|
||||
self.remove_empty_folders(torrent_id, wait_on_folder[0])
|
||||
del torrent.waiting_on_folder_rename[i]
|
||||
self.save_resume_data((torrent_id,))
|
||||
break
|
||||
@ -1012,3 +1054,9 @@ class TorrentManager(component.Component):
|
||||
except:
|
||||
return
|
||||
torrent.update_state()
|
||||
|
||||
def on_alert_file_completed(self, alert):
|
||||
log.debug("file_completed_alert: %s", alert.message())
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
component.get("EventManager").emit(
|
||||
TorrentFileCompletedEvent(torrent_id, alert.index))
|
||||
|
||||
BIN
deluge/data/pixmaps/tracker_all16.png
Normal file
BIN
deluge/data/pixmaps/tracker_all16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
deluge/data/pixmaps/tracker_warning16.png
Normal file
BIN
deluge/data/pixmaps/tracker_warning16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 683 B |
@ -1,12 +1,14 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Deluge BitTorrent Client
|
||||
GenericName=Bittorrent Client
|
||||
Comment=Transfer files using the Bittorrent protocol
|
||||
Exec=deluge-gtk
|
||||
Name=Deluge
|
||||
GenericName=BitTorrent Client
|
||||
X-GNOME-FullName=Deluge BitTorrent Client
|
||||
Comment=Download and share files over BitTorrent
|
||||
TryExec=deluge-gtk
|
||||
Exec=deluge-gtk %U
|
||||
Icon=deluge
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;
|
||||
Categories=Network;FileTransfer;P2P;GTK
|
||||
StartupNotify=true
|
||||
MimeType=application/x-bittorrent;
|
||||
MimeType=application/x-bittorrent;x-scheme-handler/magnet;
|
||||
|
||||
@ -164,6 +164,22 @@ class TorrentResumedEvent(DelugeEvent):
|
||||
"""
|
||||
self._args = [torrent_id]
|
||||
|
||||
class TorrentFileCompletedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a file completes.
|
||||
|
||||
This will only work with libtorrent 0.15 or greater.
|
||||
|
||||
"""
|
||||
def __init__(self, torrent_id, index):
|
||||
"""
|
||||
:param torrent_id: the torrent_id
|
||||
:type torrent_id: string
|
||||
:param index: the file index
|
||||
:type index: int
|
||||
"""
|
||||
self._args = [torrent_id, index]
|
||||
|
||||
class NewVersionAvailableEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a more recent version of Deluge is available.
|
||||
|
||||
@ -1,290 +1,190 @@
|
||||
deluge/configmanager.py
|
||||
deluge/httpdownloader.py
|
||||
deluge/error.py
|
||||
deluge/bencode.py
|
||||
deluge/component.py
|
||||
deluge/log.py
|
||||
deluge/metafile.py
|
||||
deluge/config.py
|
||||
deluge/main.py
|
||||
deluge/__init__.py
|
||||
deluge/common.py
|
||||
deluge/bencode.py
|
||||
deluge/pluginmanagerbase.py
|
||||
deluge/event.py
|
||||
deluge/rencode.py
|
||||
deluge/decorators.py
|
||||
deluge/_libtorrent.py
|
||||
deluge/__rpcapi.py
|
||||
deluge/__init__.py
|
||||
deluge/pluginmanagerbase.py
|
||||
deluge/config.py
|
||||
deluge/common.py
|
||||
deluge/maketorrent.py
|
||||
deluge/rencode.py
|
||||
deluge/metafile.py
|
||||
deluge/_libtorrent.py
|
||||
deluge/preferencesmanager.py
|
||||
deluge/httpdownloader.py
|
||||
deluge/__rpcapi.py
|
||||
deluge/event.py
|
||||
deluge/configmanager.py
|
||||
deluge/core/daemon.py
|
||||
deluge/core/pluginmanager.py
|
||||
deluge/core/rpcserver.py
|
||||
deluge/core/authmanager.py
|
||||
deluge/core/autoadd.py
|
||||
deluge/core/eventmanager.py
|
||||
deluge/core/__init__.py
|
||||
deluge/core/oldstateupgrader.py
|
||||
deluge/core/alertmanager.py
|
||||
deluge/core/preferencesmanager.py
|
||||
deluge/core/torrent.py
|
||||
deluge/core/filtermanager.py
|
||||
deluge/core/core.py
|
||||
deluge/core/torrentmanager.py
|
||||
deluge/ui/countries.py
|
||||
deluge/ui/client.py
|
||||
deluge/ui/Win32IconImagePlugin.py
|
||||
deluge/ui/ui.py
|
||||
deluge/ui/sessionproxy.py
|
||||
deluge/ui/__init__.py
|
||||
deluge/ui/common.py
|
||||
deluge/ui/tracker_icons.py
|
||||
deluge/ui/session.py
|
||||
deluge/ui/coreconfig.py
|
||||
deluge/ui/gtkui/pluginmanager.py
|
||||
deluge/ui/gtkui/systemtray.py
|
||||
deluge/ui/gtkui/queuedtorrents.py
|
||||
deluge/ui/gtkui/aboutdialog.py
|
||||
deluge/ui/gtkui/files_tab.py
|
||||
deluge/ui/gtkui/torrentdetails.py
|
||||
deluge/ui/gtkui/dialogs.py
|
||||
deluge/ui/gtkui/gtkui.py
|
||||
deluge/ui/gtkui/toolbar.py
|
||||
deluge/ui/gtkui/options_tab.py
|
||||
deluge/ui/gtkui/notification.py
|
||||
deluge/ui/gtkui/status_tab.py
|
||||
deluge/ui/gtkui/details_tab.py
|
||||
deluge/ui/gtkui/listview.py
|
||||
deluge/ui/gtkui/addtorrentdialog.py
|
||||
deluge/ui/gtkui/__init__.py
|
||||
deluge/ui/gtkui/filtertreeview.py
|
||||
deluge/ui/gtkui/edittrackersdialog.py
|
||||
deluge/ui/gtkui/sidebar.py
|
||||
deluge/ui/gtkui/statusbar.py
|
||||
deluge/ui/gtkui/removetorrentdialog.py
|
||||
deluge/ui/gtkui/peers_tab.py
|
||||
deluge/ui/gtkui/ipcinterface.py
|
||||
deluge/ui/gtkui/new_release_dialog.py
|
||||
deluge/ui/gtkui/common.py
|
||||
deluge/ui/gtkui/menubar.py
|
||||
deluge/ui/gtkui/connectionmanager.py
|
||||
deluge/ui/gtkui/createtorrentdialog.py
|
||||
deluge/ui/gtkui/preferences.py
|
||||
deluge/ui/gtkui/mainwindow.py
|
||||
deluge/ui/gtkui/preferences_old.py
|
||||
deluge/ui/gtkui/torrentview.py
|
||||
deluge/ui/gtkui/glade/create_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/filtertree_menu.glade
|
||||
deluge/ui/gtkui/glade/dgtkpopups.glade
|
||||
deluge/ui/gtkui/glade/torrent_menu.glade
|
||||
deluge/ui/gtkui/glade/main_window.glade
|
||||
deluge/ui/gtkui/glade/remove_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/tray_menu.glade
|
||||
deluge/ui/gtkui/glade/preferences_dialog.glade
|
||||
deluge/ui/gtkui/glade/edit_trackers.glade
|
||||
deluge/ui/gtkui/glade/move_storage_dialog.glade
|
||||
deluge/ui/gtkui/glade/connection_manager.glade
|
||||
deluge/ui/gtkui/glade/add_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/queuedtorrents.glade
|
||||
deluge/ui/console/colors.py
|
||||
deluge/ui/console/statusbars.py
|
||||
deluge/ui/console/main.py
|
||||
deluge/ui/console/eventlog.py
|
||||
deluge/ui/console/__init__.py
|
||||
deluge/ui/console/screen.py
|
||||
deluge/ui/console/commands/add.py
|
||||
deluge/ui/console/commands/info.py
|
||||
deluge/ui/console/commands/halt.py
|
||||
deluge/ui/console/commands/__init__.py
|
||||
deluge/ui/console/commands/connect.py
|
||||
deluge/ui/console/commands/resume.py
|
||||
deluge/ui/console/commands/config.py
|
||||
deluge/ui/console/commands/quit.py
|
||||
deluge/ui/console/commands/rm.py
|
||||
deluge/ui/console/commands/help.py
|
||||
deluge/ui/console/commands/pause.py
|
||||
deluge/ui/console/commands/recheck.py
|
||||
deluge/ui/console/commands/cache.py
|
||||
deluge/ui/console/commands/plugin.py
|
||||
deluge/ui/console/commands/debug.py
|
||||
deluge/ui/web/pluginmanager.py
|
||||
deluge/ui/web/web.py
|
||||
deluge/ui/web/gen_gettext.py
|
||||
deluge/ui/web/__init__.py
|
||||
deluge/ui/web/server.py
|
||||
deluge/ui/web/common.py
|
||||
deluge/ui/web/auth.py
|
||||
deluge/ui/web/json_api.py
|
||||
deluge/plugins/__init__.py
|
||||
deluge/plugins/pluginbase.py
|
||||
deluge/plugins/init.py
|
||||
deluge/plugins/feeder/setup.py
|
||||
deluge/plugins/feeder/feeder/__init__.py
|
||||
deluge/plugins/feeder/feeder/core.py
|
||||
deluge/plugins/feeder/feeder/webui.py
|
||||
deluge/plugins/feeder/build/lib/feeder/__init__.py
|
||||
deluge/plugins/feeder/build/lib/feeder/core.py
|
||||
deluge/plugins/feeder/build/lib/feeder/webui.py
|
||||
deluge/plugins/label/setup.py
|
||||
deluge/plugins/label/label/test.py
|
||||
deluge/plugins/label/label/__init__.py
|
||||
deluge/plugins/label/label/core.py
|
||||
deluge/plugins/label/label/webui.py
|
||||
deluge/plugins/label/label/gtkui/__init__.py
|
||||
deluge/plugins/label/label/gtkui/label_config.py
|
||||
deluge/plugins/label/label/gtkui/submenu.py
|
||||
deluge/plugins/label/label/gtkui/sidebar_menu.py
|
||||
deluge/plugins/label/label/data/label_pref.glade
|
||||
deluge/plugins/label/label/data/label_options.glade
|
||||
deluge/plugins/label/build/lib/label/test.py
|
||||
deluge/plugins/label/build/lib/label/__init__.py
|
||||
deluge/plugins/label/build/lib/label/core.py
|
||||
deluge/plugins/label/build/lib/label/webui.py
|
||||
deluge/plugins/label/build/lib/label/gtkui/__init__.py
|
||||
deluge/plugins/label/build/lib/label/gtkui/label_config.py
|
||||
deluge/plugins/label/build/lib/label/gtkui/submenu.py
|
||||
deluge/plugins/label/build/lib/label/gtkui/sidebar_menu.py
|
||||
deluge/plugins/label/build/lib/label/data/label_pref.glade
|
||||
deluge/plugins/label/build/lib/label/data/label_options.glade
|
||||
deluge/plugins/autoadd/setup.py
|
||||
deluge/plugins/autoadd/autoadd/__init__.py
|
||||
deluge/plugins/autoadd/autoadd/common.py
|
||||
deluge/plugins/autoadd/autoadd/core.py
|
||||
deluge/plugins/autoadd/autoadd/webui.py
|
||||
deluge/plugins/autoadd/autoadd/gtkui.py
|
||||
deluge/plugins/autoadd/autoadd/data/config.glade
|
||||
deluge/plugins/autoadd/autoadd/data/autoadd_options.glade
|
||||
deluge/plugins/autoadd/build/lib/autoadd/__init__.py
|
||||
deluge/plugins/autoadd/build/lib/autoadd/common.py
|
||||
deluge/plugins/autoadd/build/lib/autoadd/core.py
|
||||
deluge/plugins/autoadd/build/lib/autoadd/webui.py
|
||||
deluge/plugins/autoadd/build/lib/autoadd/gtkui.py
|
||||
deluge/plugins/autoadd/build/lib/autoadd/data/config.glade
|
||||
deluge/plugins/autoadd/build/lib/autoadd/data/autoadd_options.glade
|
||||
deluge/plugins/scheduler/setup.py
|
||||
deluge/plugins/scheduler/scheduler/__init__.py
|
||||
deluge/plugins/scheduler/scheduler/common.py
|
||||
deluge/plugins/scheduler/scheduler/core.py
|
||||
deluge/plugins/scheduler/scheduler/webui.py
|
||||
deluge/plugins/scheduler/scheduler/gtkui.py
|
||||
deluge/plugins/scheduler/build/lib/scheduler/__init__.py
|
||||
deluge/plugins/scheduler/build/lib/scheduler/common.py
|
||||
deluge/plugins/scheduler/build/lib/scheduler/core.py
|
||||
deluge/plugins/scheduler/build/lib/scheduler/webui.py
|
||||
deluge/plugins/scheduler/build/lib/scheduler/gtkui.py
|
||||
deluge/plugins/notifications/setup.py
|
||||
deluge/plugins/notifications/notifications/test.py
|
||||
deluge/plugins/notifications/notifications/gtkui.py
|
||||
deluge/plugins/notifications/notifications/__init__.py
|
||||
deluge/plugins/notifications/notifications/common.py
|
||||
deluge/plugins/notifications/notifications/core.py
|
||||
deluge/plugins/notifications/notifications/webui.py
|
||||
deluge/plugins/notifications/notifications/gtkui.py
|
||||
deluge/plugins/notifications/notifications/core.py
|
||||
deluge/plugins/notifications/notifications/test.py
|
||||
deluge/plugins/notifications/notifications/data/config.glade
|
||||
deluge/plugins/notifications/build/lib/notifications/test.py
|
||||
deluge/plugins/notifications/build/lib/notifications/__init__.py
|
||||
deluge/plugins/notifications/build/lib/notifications/common.py
|
||||
deluge/plugins/notifications/build/lib/notifications/core.py
|
||||
deluge/plugins/notifications/build/lib/notifications/webui.py
|
||||
deluge/plugins/notifications/build/lib/notifications/gtkui.py
|
||||
deluge/plugins/notifications/build/lib/notifications/data/config.glade
|
||||
deluge/plugins/stats/setup.py
|
||||
deluge/plugins/stats/stats/test_total.py
|
||||
deluge/plugins/stats/stats/test.py
|
||||
deluge/plugins/stats/stats/__init__.py
|
||||
deluge/plugins/stats/stats/graph.py
|
||||
deluge/plugins/stats/stats/common.py
|
||||
deluge/plugins/stats/stats/core.py
|
||||
deluge/plugins/stats/stats/webui.py
|
||||
deluge/plugins/stats/stats/gtkui.py
|
||||
deluge/plugins/stats/stats/data/tabs.glade
|
||||
deluge/plugins/stats/stats/data/config.glade
|
||||
deluge/plugins/stats/build/lib/stats/test_total.py
|
||||
deluge/plugins/stats/build/lib/stats/test.py
|
||||
deluge/plugins/stats/build/lib/stats/__init__.py
|
||||
deluge/plugins/stats/build/lib/stats/graph.py
|
||||
deluge/plugins/stats/build/lib/stats/common.py
|
||||
deluge/plugins/stats/build/lib/stats/core.py
|
||||
deluge/plugins/stats/build/lib/stats/webui.py
|
||||
deluge/plugins/stats/build/lib/stats/gtkui.py
|
||||
deluge/plugins/stats/build/lib/stats/data/tabs.glade
|
||||
deluge/plugins/stats/build/lib/stats/data/config.glade
|
||||
deluge/plugins/freespace/build/lib/freespace/gtkui.py
|
||||
deluge/plugins/freespace/build/lib/freespace/__init__.py
|
||||
deluge/plugins/freespace/build/lib/freespace/common.py
|
||||
deluge/plugins/freespace/build/lib/freespace/webui.py
|
||||
deluge/plugins/freespace/build/lib/freespace/core.py
|
||||
deluge/plugins/freespace/build/lib/freespace/data/config.glade
|
||||
deluge/plugins/webui/setup.py
|
||||
deluge/plugins/webui/webui/gtkui.py
|
||||
deluge/plugins/webui/webui/__init__.py
|
||||
deluge/plugins/webui/webui/common.py
|
||||
deluge/plugins/webui/webui/core.py
|
||||
deluge/plugins/webui/webui/gtkui.py
|
||||
deluge/plugins/webui/webui/data/config.glade
|
||||
deluge/plugins/webui/build/lib/webui/__init__.py
|
||||
deluge/plugins/webui/build/lib/webui/common.py
|
||||
deluge/plugins/webui/build/lib/webui/core.py
|
||||
deluge/plugins/webui/build/lib/webui/gtkui.py
|
||||
deluge/plugins/webui/build/lib/webui/data/config.glade
|
||||
deluge/plugins/extractor/setup.py
|
||||
deluge/plugins/extractor/build/lib/extractor/__init__.py
|
||||
deluge/plugins/extractor/build/lib/extractor/common.py
|
||||
deluge/plugins/extractor/build/lib/extractor/core.py
|
||||
deluge/plugins/extractor/build/lib/extractor/webui.py
|
||||
deluge/plugins/extractor/build/lib/extractor/gtkui.py
|
||||
deluge/plugins/extractor/build/lib/extractor/data/extractor_prefs.glade
|
||||
deluge/plugins/extractor/extractor/__init__.py
|
||||
deluge/plugins/extractor/extractor/common.py
|
||||
deluge/plugins/extractor/extractor/core.py
|
||||
deluge/plugins/extractor/extractor/webui.py
|
||||
deluge/plugins/extractor/extractor/gtkui.py
|
||||
deluge/plugins/extractor/extractor/data/extractor_prefs.glade
|
||||
deluge/plugins/execute/setup.py
|
||||
deluge/plugins/execute/build/lib/execute/__init__.py
|
||||
deluge/plugins/execute/build/lib/execute/common.py
|
||||
deluge/plugins/execute/build/lib/execute/core.py
|
||||
deluge/plugins/execute/build/lib/execute/webui.py
|
||||
deluge/plugins/execute/build/lib/execute/gtkui.py
|
||||
deluge/plugins/execute/build/lib/execute/data/execute_prefs.glade
|
||||
deluge/plugins/execute/execute/__init__.py
|
||||
deluge/plugins/execute/execute/common.py
|
||||
deluge/plugins/execute/execute/core.py
|
||||
deluge/plugins/execute/execute/webui.py
|
||||
deluge/plugins/execute/execute/gtkui.py
|
||||
deluge/plugins/execute/execute/data/execute_prefs.glade
|
||||
deluge/plugins/example/setup.py
|
||||
deluge/plugins/example/build/lib/example/__init__.py
|
||||
deluge/plugins/example/build/lib/example/common.py
|
||||
deluge/plugins/example/build/lib/example/core.py
|
||||
deluge/plugins/example/build/lib/example/webui.py
|
||||
deluge/plugins/example/build/lib/example/gtkui.py
|
||||
deluge/plugins/example/example/__init__.py
|
||||
deluge/plugins/example/example/common.py
|
||||
deluge/plugins/example/example/core.py
|
||||
deluge/plugins/example/example/webui.py
|
||||
deluge/plugins/example/example/gtkui.py
|
||||
deluge/plugins/freespace/setup.py
|
||||
deluge/plugins/freespace/build/lib/freespace/__init__.py
|
||||
deluge/plugins/freespace/build/lib/freespace/common.py
|
||||
deluge/plugins/freespace/build/lib/freespace/core.py
|
||||
deluge/plugins/freespace/build/lib/freespace/webui.py
|
||||
deluge/plugins/freespace/build/lib/freespace/gtkui.py
|
||||
deluge/plugins/freespace/build/lib/freespace/data/config.glade
|
||||
deluge/plugins/freespace/freespace/__init__.py
|
||||
deluge/plugins/freespace/freespace/common.py
|
||||
deluge/plugins/freespace/freespace/core.py
|
||||
deluge/plugins/freespace/freespace/webui.py
|
||||
deluge/plugins/freespace/freespace/gtkui.py
|
||||
deluge/plugins/freespace/freespace/data/config.glade
|
||||
deluge/plugins/autoadd/setup.py
|
||||
deluge/plugins/autoadd/autoadd/gtkui.py
|
||||
deluge/plugins/autoadd/autoadd/__init__.py
|
||||
deluge/plugins/autoadd/autoadd/common.py
|
||||
deluge/plugins/autoadd/autoadd/webui.py
|
||||
deluge/plugins/autoadd/autoadd/core.py
|
||||
deluge/plugins/autoadd/autoadd/data/autoadd_options.glade
|
||||
deluge/plugins/autoadd/autoadd/data/config.glade
|
||||
deluge/plugins/label/setup.py
|
||||
deluge/plugins/label/label/__init__.py
|
||||
deluge/plugins/label/label/webui.py
|
||||
deluge/plugins/label/label/core.py
|
||||
deluge/plugins/label/label/test.py
|
||||
deluge/plugins/label/label/gtkui/sidebar_menu.py
|
||||
deluge/plugins/label/label/gtkui/submenu.py
|
||||
deluge/plugins/label/label/gtkui/__init__.py
|
||||
deluge/plugins/label/label/gtkui/label_config.py
|
||||
deluge/plugins/label/label/data/label_options.glade
|
||||
deluge/plugins/label/label/data/label_pref.glade
|
||||
deluge/plugins/blocklist/setup.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/peerguardian.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/decompressers.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/detect.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/readers.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/__init__.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/common.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/core.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/webui.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/gtkui.py
|
||||
deluge/plugins/blocklist/build/lib/blocklist/data/blocklist_pref.glade
|
||||
deluge/plugins/blocklist/blocklist/peerguardian.py
|
||||
deluge/plugins/blocklist/blocklist/decompressers.py
|
||||
deluge/plugins/blocklist/blocklist/detect.py
|
||||
deluge/plugins/blocklist/blocklist/readers.py
|
||||
deluge/plugins/blocklist/blocklist/gtkui.py
|
||||
deluge/plugins/blocklist/blocklist/__init__.py
|
||||
deluge/plugins/blocklist/blocklist/common.py
|
||||
deluge/plugins/blocklist/blocklist/core.py
|
||||
deluge/plugins/blocklist/blocklist/peerguardian.py
|
||||
deluge/plugins/blocklist/blocklist/decompressers.py
|
||||
deluge/plugins/blocklist/blocklist/webui.py
|
||||
deluge/plugins/blocklist/blocklist/gtkui.py
|
||||
deluge/plugins/blocklist/blocklist/detect.py
|
||||
deluge/plugins/blocklist/blocklist/core.py
|
||||
deluge/plugins/blocklist/blocklist/data/blocklist_pref.glade
|
||||
deluge/core/eventmanager.py
|
||||
deluge/core/autoadd.py
|
||||
deluge/core/authmanager.py
|
||||
deluge/core/rpcserver.py
|
||||
deluge/core/torrentmanager.py
|
||||
deluge/core/oldstateupgrader.py
|
||||
deluge/core/__init__.py
|
||||
deluge/core/torrent.py
|
||||
deluge/core/pluginmanager.py
|
||||
deluge/core/core.py
|
||||
deluge/core/daemon.py
|
||||
deluge/core/alertmanager.py
|
||||
deluge/core/preferencesmanager.py
|
||||
deluge/core/filtermanager.py
|
||||
deluge/ui/sessionproxy.py
|
||||
deluge/ui/ui.py
|
||||
deluge/ui/session.py
|
||||
deluge/ui/tracker_icons.py
|
||||
deluge/ui/__init__.py
|
||||
deluge/ui/common.py
|
||||
deluge/ui/Win32IconImagePlugin.py
|
||||
deluge/ui/client.py
|
||||
deluge/ui/countries.py
|
||||
deluge/ui/coreconfig.py
|
||||
deluge/ui/web/server.py
|
||||
deluge/ui/web/web.py
|
||||
deluge/ui/web/__init__.py
|
||||
deluge/ui/web/common.py
|
||||
deluge/ui/web/pluginmanager.py
|
||||
deluge/ui/web/gen_gettext.py
|
||||
deluge/ui/web/auth.py
|
||||
deluge/ui/web/json_api.py
|
||||
deluge/ui/gtkui/connectionmanager.py
|
||||
deluge/ui/gtkui/torrentdetails.py
|
||||
deluge/ui/gtkui/queuedtorrents.py
|
||||
deluge/ui/gtkui/addtorrentdialog.py
|
||||
deluge/ui/gtkui/__init__.py
|
||||
deluge/ui/gtkui/status_tab.py
|
||||
deluge/ui/gtkui/preferences.py
|
||||
deluge/ui/gtkui/mainwindow.py
|
||||
deluge/ui/gtkui/notification.py
|
||||
deluge/ui/gtkui/ipcinterface.py
|
||||
deluge/ui/gtkui/createtorrentdialog.py
|
||||
deluge/ui/gtkui/torrentview.py
|
||||
deluge/ui/gtkui/listview.py
|
||||
deluge/ui/gtkui/systemtray.py
|
||||
deluge/ui/gtkui/common.py
|
||||
deluge/ui/gtkui/pluginmanager.py
|
||||
deluge/ui/gtkui/menubar.py
|
||||
deluge/ui/gtkui/sidebar.py
|
||||
deluge/ui/gtkui/statusbar.py
|
||||
deluge/ui/gtkui/filtertreeview.py
|
||||
deluge/ui/gtkui/new_release_dialog.py
|
||||
deluge/ui/gtkui/options_tab.py
|
||||
deluge/ui/gtkui/peers_tab.py
|
||||
deluge/ui/gtkui/details_tab.py
|
||||
deluge/ui/gtkui/files_tab.py
|
||||
deluge/ui/gtkui/gtkui.py
|
||||
deluge/ui/gtkui/edittrackersdialog.py
|
||||
deluge/ui/gtkui/removetorrentdialog.py
|
||||
deluge/ui/gtkui/toolbar.py
|
||||
deluge/ui/gtkui/dialogs.py
|
||||
deluge/ui/gtkui/aboutdialog.py
|
||||
deluge/ui/gtkui/glade/filtertree_menu.glade
|
||||
deluge/ui/gtkui/glade/main_window.glade
|
||||
deluge/ui/gtkui/glade/remove_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/create_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/connection_manager.glade
|
||||
deluge/ui/gtkui/glade/preferences_dialog.glade
|
||||
deluge/ui/gtkui/glade/torrent_menu.glade
|
||||
deluge/ui/gtkui/glade/queuedtorrents.glade
|
||||
deluge/ui/gtkui/glade/move_storage_dialog.glade
|
||||
deluge/ui/gtkui/glade/add_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/dgtkpopups.glade
|
||||
deluge/ui/gtkui/glade/tray_menu.glade
|
||||
deluge/ui/gtkui/glade/edit_trackers.glade
|
||||
deluge/ui/console/colors.py
|
||||
deluge/ui/console/eventlog.py
|
||||
deluge/ui/console/main.py
|
||||
deluge/ui/console/__init__.py
|
||||
deluge/ui/console/statusbars.py
|
||||
deluge/ui/console/screen.py
|
||||
deluge/ui/console/commands/plugin.py
|
||||
deluge/ui/console/commands/info.py
|
||||
deluge/ui/console/commands/recheck.py
|
||||
deluge/ui/console/commands/quit.py
|
||||
deluge/ui/console/commands/connect.py
|
||||
deluge/ui/console/commands/help.py
|
||||
deluge/ui/console/commands/add.py
|
||||
deluge/ui/console/commands/config.py
|
||||
deluge/ui/console/commands/__init__.py
|
||||
deluge/ui/console/commands/cache.py
|
||||
deluge/ui/console/commands/debug.py
|
||||
deluge/ui/console/commands/pause.py
|
||||
deluge/ui/console/commands/rm.py
|
||||
deluge/ui/console/commands/halt.py
|
||||
deluge/ui/console/commands/resume.py
|
||||
deluge/plugins/extractor/setup.py
|
||||
deluge/plugins/extractor/extractor/gtkui.py
|
||||
deluge/plugins/extractor/extractor/__init__.py
|
||||
deluge/plugins/extractor/extractor/common.py
|
||||
deluge/plugins/extractor/extractor/webui.py
|
||||
deluge/plugins/extractor/extractor/core.py
|
||||
deluge/plugins/extractor/extractor/data/extractor_prefs.glade
|
||||
deluge/plugins/execute/setup.py
|
||||
deluge/plugins/execute/execute/gtkui.py
|
||||
deluge/plugins/execute/execute/__init__.py
|
||||
deluge/plugins/execute/execute/common.py
|
||||
deluge/plugins/execute/execute/webui.py
|
||||
deluge/plugins/execute/execute/core.py
|
||||
deluge/plugins/execute/execute/data/execute_prefs.glade
|
||||
deluge/plugins/scheduler/setup.py
|
||||
deluge/plugins/scheduler/scheduler/gtkui.py
|
||||
deluge/plugins/scheduler/scheduler/__init__.py
|
||||
deluge/plugins/scheduler/scheduler/common.py
|
||||
deluge/plugins/scheduler/scheduler/webui.py
|
||||
deluge/plugins/scheduler/scheduler/core.py
|
||||
|
||||
6142
deluge/i18n/ast.po
6142
deluge/i18n/ast.po
File diff suppressed because it is too large
Load Diff
6719
deluge/i18n/be.po
6719
deluge/i18n/be.po
File diff suppressed because it is too large
Load Diff
5161
deluge/i18n/bg.po
5161
deluge/i18n/bg.po
File diff suppressed because it is too large
Load Diff
5172
deluge/i18n/bn.po
5172
deluge/i18n/bn.po
File diff suppressed because it is too large
Load Diff
5940
deluge/i18n/bs.po
5940
deluge/i18n/bs.po
File diff suppressed because it is too large
Load Diff
5175
deluge/i18n/ca.po
5175
deluge/i18n/ca.po
File diff suppressed because it is too large
Load Diff
5283
deluge/i18n/cs.po
5283
deluge/i18n/cs.po
File diff suppressed because it is too large
Load Diff
5173
deluge/i18n/cy.po
5173
deluge/i18n/cy.po
File diff suppressed because it is too large
Load Diff
4515
deluge/i18n/da.po
4515
deluge/i18n/da.po
File diff suppressed because it is too large
Load Diff
5296
deluge/i18n/de.po
5296
deluge/i18n/de.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
5796
deluge/i18n/el.po
5796
deluge/i18n/el.po
File diff suppressed because it is too large
Load Diff
5249
deluge/i18n/en_AU.po
5249
deluge/i18n/en_AU.po
File diff suppressed because it is too large
Load Diff
5581
deluge/i18n/en_CA.po
5581
deluge/i18n/en_CA.po
File diff suppressed because it is too large
Load Diff
5761
deluge/i18n/en_GB.po
5761
deluge/i18n/en_GB.po
File diff suppressed because it is too large
Load Diff
5154
deluge/i18n/eo.po
5154
deluge/i18n/eo.po
File diff suppressed because it is too large
Load Diff
5245
deluge/i18n/es.po
5245
deluge/i18n/es.po
File diff suppressed because it is too large
Load Diff
5004
deluge/i18n/et.po
5004
deluge/i18n/et.po
File diff suppressed because it is too large
Load Diff
4276
deluge/i18n/eu.po
4276
deluge/i18n/eu.po
File diff suppressed because it is too large
Load Diff
6178
deluge/i18n/fa.po
6178
deluge/i18n/fa.po
File diff suppressed because it is too large
Load Diff
5229
deluge/i18n/fi.po
5229
deluge/i18n/fi.po
File diff suppressed because it is too large
Load Diff
3754
deluge/i18n/fo.po
Normal file
3754
deluge/i18n/fo.po
Normal file
File diff suppressed because it is too large
Load Diff
5302
deluge/i18n/fr.po
5302
deluge/i18n/fr.po
File diff suppressed because it is too large
Load Diff
5979
deluge/i18n/fy.po
5979
deluge/i18n/fy.po
File diff suppressed because it is too large
Load Diff
4545
deluge/i18n/gl.po
4545
deluge/i18n/gl.po
File diff suppressed because it is too large
Load Diff
5036
deluge/i18n/he.po
5036
deluge/i18n/he.po
File diff suppressed because it is too large
Load Diff
4642
deluge/i18n/hi.po
4642
deluge/i18n/hi.po
File diff suppressed because it is too large
Load Diff
5894
deluge/i18n/hr.po
5894
deluge/i18n/hr.po
File diff suppressed because it is too large
Load Diff
5576
deluge/i18n/hu.po
5576
deluge/i18n/hu.po
File diff suppressed because it is too large
Load Diff
4644
deluge/i18n/id.po
4644
deluge/i18n/id.po
File diff suppressed because it is too large
Load Diff
5192
deluge/i18n/is.po
5192
deluge/i18n/is.po
File diff suppressed because it is too large
Load Diff
5621
deluge/i18n/it.po
5621
deluge/i18n/it.po
File diff suppressed because it is too large
Load Diff
5097
deluge/i18n/iu.po
5097
deluge/i18n/iu.po
File diff suppressed because it is too large
Load Diff
5137
deluge/i18n/ja.po
5137
deluge/i18n/ja.po
File diff suppressed because it is too large
Load Diff
5265
deluge/i18n/ka.po
5265
deluge/i18n/ka.po
File diff suppressed because it is too large
Load Diff
5189
deluge/i18n/kk.po
5189
deluge/i18n/kk.po
File diff suppressed because it is too large
Load Diff
5237
deluge/i18n/kn.po
5237
deluge/i18n/kn.po
File diff suppressed because it is too large
Load Diff
5084
deluge/i18n/ko.po
5084
deluge/i18n/ko.po
File diff suppressed because it is too large
Load Diff
5123
deluge/i18n/ku.po
5123
deluge/i18n/ku.po
File diff suppressed because it is too large
Load Diff
5102
deluge/i18n/la.po
5102
deluge/i18n/la.po
File diff suppressed because it is too large
Load Diff
3754
deluge/i18n/lb.po
Normal file
3754
deluge/i18n/lb.po
Normal file
File diff suppressed because it is too large
Load Diff
5692
deluge/i18n/lt.po
5692
deluge/i18n/lt.po
File diff suppressed because it is too large
Load Diff
5748
deluge/i18n/lv.po
5748
deluge/i18n/lv.po
File diff suppressed because it is too large
Load Diff
5641
deluge/i18n/mk.po
5641
deluge/i18n/mk.po
File diff suppressed because it is too large
Load Diff
3754
deluge/i18n/ml.po
Normal file
3754
deluge/i18n/ml.po
Normal file
File diff suppressed because it is too large
Load Diff
5693
deluge/i18n/ms.po
5693
deluge/i18n/ms.po
File diff suppressed because it is too large
Load Diff
5596
deluge/i18n/nb.po
5596
deluge/i18n/nb.po
File diff suppressed because it is too large
Load Diff
5108
deluge/i18n/nds.po
5108
deluge/i18n/nds.po
File diff suppressed because it is too large
Load Diff
5784
deluge/i18n/nl.po
5784
deluge/i18n/nl.po
File diff suppressed because it is too large
Load Diff
3763
deluge/i18n/nn.po
Normal file
3763
deluge/i18n/nn.po
Normal file
File diff suppressed because it is too large
Load Diff
3783
deluge/i18n/oc.po
Normal file
3783
deluge/i18n/oc.po
Normal file
File diff suppressed because it is too large
Load Diff
5699
deluge/i18n/pl.po
5699
deluge/i18n/pl.po
File diff suppressed because it is too large
Load Diff
5098
deluge/i18n/pms.po
5098
deluge/i18n/pms.po
File diff suppressed because it is too large
Load Diff
5734
deluge/i18n/pt.po
5734
deluge/i18n/pt.po
File diff suppressed because it is too large
Load Diff
5810
deluge/i18n/pt_BR.po
5810
deluge/i18n/pt_BR.po
File diff suppressed because it is too large
Load Diff
5801
deluge/i18n/ro.po
5801
deluge/i18n/ro.po
File diff suppressed because it is too large
Load Diff
5352
deluge/i18n/ru.po
5352
deluge/i18n/ru.po
File diff suppressed because it is too large
Load Diff
5097
deluge/i18n/si.po
5097
deluge/i18n/si.po
File diff suppressed because it is too large
Load Diff
5692
deluge/i18n/sk.po
5692
deluge/i18n/sk.po
File diff suppressed because it is too large
Load Diff
4988
deluge/i18n/sl.po
4988
deluge/i18n/sl.po
File diff suppressed because it is too large
Load Diff
5070
deluge/i18n/sr.po
5070
deluge/i18n/sr.po
File diff suppressed because it is too large
Load Diff
5736
deluge/i18n/sv.po
5736
deluge/i18n/sv.po
File diff suppressed because it is too large
Load Diff
5113
deluge/i18n/ta.po
5113
deluge/i18n/ta.po
File diff suppressed because it is too large
Load Diff
3754
deluge/i18n/te.po
Normal file
3754
deluge/i18n/te.po
Normal file
File diff suppressed because it is too large
Load Diff
4223
deluge/i18n/th.po
4223
deluge/i18n/th.po
File diff suppressed because it is too large
Load Diff
5098
deluge/i18n/tl.po
5098
deluge/i18n/tl.po
File diff suppressed because it is too large
Load Diff
5097
deluge/i18n/tlh.po
5097
deluge/i18n/tlh.po
File diff suppressed because it is too large
Load Diff
4576
deluge/i18n/tr.po
4576
deluge/i18n/tr.po
File diff suppressed because it is too large
Load Diff
7305
deluge/i18n/uk.po
7305
deluge/i18n/uk.po
File diff suppressed because it is too large
Load Diff
3754
deluge/i18n/ur.po
Normal file
3754
deluge/i18n/ur.po
Normal file
File diff suppressed because it is too large
Load Diff
5124
deluge/i18n/vi.po
5124
deluge/i18n/vi.po
File diff suppressed because it is too large
Load Diff
5618
deluge/i18n/zh_CN.po
5618
deluge/i18n/zh_CN.po
File diff suppressed because it is too large
Load Diff
5123
deluge/i18n/zh_HK.po
5123
deluge/i18n/zh_HK.po
File diff suppressed because it is too large
Load Diff
7509
deluge/i18n/zh_TW.po
7509
deluge/i18n/zh_TW.po
File diff suppressed because it is too large
Load Diff
@ -197,6 +197,11 @@ this should be an IP address", metavar="IFACE",
|
||||
write_pidfile()
|
||||
|
||||
# Setup the logger
|
||||
try:
|
||||
# Try to make the logfile's directory if it doesn't exist
|
||||
os.makedirs(os.path.abspath(os.path.dirname(options.logfile)))
|
||||
except:
|
||||
pass
|
||||
deluge.log.setupLogger(level=options.loglevel, filename=options.logfile)
|
||||
from deluge.log import LOG as log
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ DEFAULT_PREFS = {
|
||||
OPTIONS_AVAILABLE = { #option: builtin
|
||||
"enabled":False,
|
||||
"path":False,
|
||||
"append_extension":False,
|
||||
"abspath":False,
|
||||
"download_location":True,
|
||||
"max_download_speed":True,
|
||||
@ -70,15 +71,14 @@ OPTIONS_AVAILABLE = { #option: builtin
|
||||
"move_completed":True,
|
||||
"move_completed_path":True,
|
||||
"label":False,
|
||||
"add_paused":True
|
||||
"add_paused":True,
|
||||
"queue_to_top":False
|
||||
}
|
||||
|
||||
MAX_NUM_ATTEMPTS = 10
|
||||
|
||||
class AutoaddOptionsChangedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a new command is added.
|
||||
"""
|
||||
"""Emitted when the options for the plugin are changed."""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@ -93,21 +93,20 @@ class Core(CorePluginBase):
|
||||
self.config = deluge.configmanager.ConfigManager("autoadd.conf", DEFAULT_PREFS)
|
||||
self.watchdirs = self.config["watchdirs"]
|
||||
self.core_cfg = deluge.configmanager.ConfigManager("core.conf")
|
||||
|
||||
|
||||
# A list of filenames
|
||||
self.invalid_torrents = []
|
||||
# Filename:Attempts
|
||||
self.attempts = {}
|
||||
|
||||
# Dict of Filename:Attempts
|
||||
self.invalid_torrents = {}
|
||||
# Loopingcall timers for each enabled watchdir
|
||||
self.update_timers = {}
|
||||
# If core autoadd folder is enabled, move it to the plugin
|
||||
if self.core_cfg.config.get('autoadd_enable'):
|
||||
# Disable core autoadd
|
||||
self.core_cfg['autoadd_enable'] = False
|
||||
self.core_cfg.save()
|
||||
# Check if core autoadd folder is already added in plugin
|
||||
for watchdir in self.watchdirs:
|
||||
if os.path.abspath(self.core_cfg['autoadd_location']) == watchdir['abspath']:
|
||||
watchdir['enabled'] = True
|
||||
break
|
||||
else:
|
||||
# didn't find core watchdir, add it
|
||||
@ -125,18 +124,15 @@ class Core(CorePluginBase):
|
||||
for loopingcall in self.update_timers.itervalues():
|
||||
loopingcall.stop()
|
||||
self.config.save()
|
||||
pass
|
||||
|
||||
def update(self):
|
||||
pass
|
||||
|
||||
@export()
|
||||
def set_options(self, watchdir_id, options):
|
||||
"""
|
||||
update the options for a watch folder
|
||||
"""
|
||||
"""Update the options for a watch folder."""
|
||||
watchdir_id = str(watchdir_id)
|
||||
options = self._clean_unicode(options)
|
||||
options = self._make_unicode(options)
|
||||
CheckInput(watchdir_id in self.watchdirs , _("Watch folder does not exist."))
|
||||
if options.has_key('path'):
|
||||
options['abspath'] = os.path.abspath(options['path'])
|
||||
@ -146,7 +142,7 @@ class Core(CorePluginBase):
|
||||
raise Exception("Path is already being watched.")
|
||||
for key in options.keys():
|
||||
if not key in OPTIONS_AVAILABLE:
|
||||
if not key in [key+'_toggle' for key in OPTIONS_AVAILABLE.iterkeys()]:
|
||||
if not key in [key2+'_toggle' for key2 in OPTIONS_AVAILABLE.iterkeys()]:
|
||||
raise Exception("autoadd: Invalid options key:%s" % key)
|
||||
#disable the watch loop if it was active
|
||||
if watchdir_id in self.update_timers:
|
||||
@ -177,33 +173,29 @@ class Core(CorePluginBase):
|
||||
return filedump
|
||||
|
||||
def update_watchdir(self, watchdir_id):
|
||||
"""Check the watch folder for new torrents to add."""
|
||||
watchdir_id = str(watchdir_id)
|
||||
watchdir = self.watchdirs[watchdir_id]
|
||||
if not watchdir['enabled']:
|
||||
# We shouldn't be updating because this watchdir is not enabled
|
||||
#disable the looping call
|
||||
self.update_timers[watchdir_id].stop()
|
||||
del self.update_timers[watchdir_id]
|
||||
self.disable_watchdir(watchdir_id)
|
||||
return
|
||||
|
||||
# Check the auto add folder for new torrents to add
|
||||
if not os.path.isdir(watchdir["abspath"]):
|
||||
log.warning("Invalid AutoAdd folder: %s", watchdir["abspath"])
|
||||
#disable the looping call
|
||||
watchdir['enabled'] = False
|
||||
self.update_timers[watchdir_id].stop()
|
||||
del self.update_timers[watchdir_id]
|
||||
self.disable_watchdir(watchdir_id)
|
||||
return
|
||||
|
||||
#Generate options dict for watchdir
|
||||
opts={}
|
||||
if watchdir.get('stop_at_ratio_toggle'):
|
||||
watchdir['stop_ratio_toggle'] = True
|
||||
# Generate options dict for watchdir
|
||||
opts = {}
|
||||
if 'stop_at_ratio_toggle' in watchdir:
|
||||
watchdir['stop_ratio_toggle'] = watchdir['stop_at_ratio_toggle']
|
||||
# We default to True wher reading _toggle values, so a config
|
||||
# without them is valid, and applies all its settings.
|
||||
for option, value in watchdir.iteritems():
|
||||
if OPTIONS_AVAILABLE.get(option):
|
||||
if watchdir.get(option+'_toggle', True):
|
||||
opts[option] = value
|
||||
opts = self._clean_unicode(opts)
|
||||
for filename in os.listdir(watchdir["abspath"]):
|
||||
if filename.split(".")[-1] == "torrent":
|
||||
try:
|
||||
@ -219,49 +211,73 @@ class Core(CorePluginBase):
|
||||
# torrents may not be fully saved during the pass.
|
||||
log.debug("Torrent is invalid: %s", e)
|
||||
if filename in self.invalid_torrents:
|
||||
self.attempts[filename] += 1
|
||||
if self.attempts[filename] >= MAX_NUM_ATTEMPTS:
|
||||
self.invalid_torrents[filename] += 1
|
||||
if self.invalid_torrents[filename] >= MAX_NUM_ATTEMPTS:
|
||||
os.rename(filepath, filepath + ".invalid")
|
||||
del self.attempts[filename]
|
||||
self.invalid_torrents.remove(filename)
|
||||
del self.invalid_torrents[filename]
|
||||
else:
|
||||
self.invalid_torrents.append(filename)
|
||||
self.attempts[filename] = 1
|
||||
self.invalid_torrents[filename] = 1
|
||||
continue
|
||||
|
||||
# The torrent looks good, so lets add it to the session
|
||||
# The torrent looks good, so lets add it to the session.
|
||||
torrent_id = component.get("TorrentManager").add(filedump=filedump, filename=filename, options=opts)
|
||||
if ('Label' in component.get("CorePluginManager").get_enabled_plugins()) and torrent_id:
|
||||
if watchdir.get('label_toggle', True) and watchdir.get('label'):
|
||||
label = component.get("CorePlugin.Label")
|
||||
if not watchdir['label'] in label.get_labels():
|
||||
label.add(watchdir['label'])
|
||||
label.set_torrent(torrent_id, watchdir['label'])
|
||||
os.remove(filepath)
|
||||
# If the torrent added successfully, set the extra options.
|
||||
if torrent_id:
|
||||
if 'Label' in component.get("CorePluginManager").get_enabled_plugins():
|
||||
if watchdir.get('label_toggle', True) and watchdir.get('label'):
|
||||
label = component.get("CorePlugin.Label")
|
||||
if not watchdir['label'] in label.get_labels():
|
||||
label.add(watchdir['label'])
|
||||
label.set_torrent(torrent_id, watchdir['label'])
|
||||
if watchdir.get('queue_to_top_toggle', True) and 'queue_to_top' in watchdir:
|
||||
if watchdir['queue_to_top']:
|
||||
component.get("TorrentManager").queue_top(torrent_id)
|
||||
else:
|
||||
component.get("TorrentManager").queue_bottom(torrent_id)
|
||||
# Rename or delete the torrent once added to deluge.
|
||||
if watchdir.get('append_extension_toggle'):
|
||||
if not watchdir.get('append_extension'):
|
||||
watchdir['append_extension'] = ".added"
|
||||
os.rename(filepath, filepath + watchdir['append_extension'])
|
||||
else:
|
||||
os.remove(filepath)
|
||||
|
||||
def on_update_watchdir_error(self, failure, watchdir_id):
|
||||
"""Disables any watch folders with unhandled exceptions."""
|
||||
self.disable_watchdir(watchdir_id)
|
||||
log.error("Disabling '%s', error during update: %s" % (self.watchdirs[watchdir_id]["path"], failure))
|
||||
|
||||
@export
|
||||
def enable_watchdir(self, watchdir_id):
|
||||
watchdir_id = str(watchdir_id)
|
||||
self.watchdirs[watchdir_id]['enabled'] = True
|
||||
#Enable the looping call
|
||||
self.update_timers[watchdir_id] = LoopingCall(self.update_watchdir, watchdir_id)
|
||||
self.update_timers[watchdir_id].start(5)
|
||||
self.config.save()
|
||||
component.get("EventManager").emit(AutoaddOptionsChangedEvent())
|
||||
# Enable the looping call
|
||||
if watchdir_id not in self.update_timers or not self.update_timers[watchdir_id].running:
|
||||
self.update_timers[watchdir_id] = LoopingCall(self.update_watchdir, watchdir_id)
|
||||
self.update_timers[watchdir_id].start(5).addErrback(self.on_update_watchdir_error, watchdir_id)
|
||||
# Update the config
|
||||
if not self.watchdirs[watchdir_id]['enabled']:
|
||||
self.watchdirs[watchdir_id]['enabled'] = True
|
||||
self.config.save()
|
||||
component.get("EventManager").emit(AutoaddOptionsChangedEvent())
|
||||
|
||||
@export
|
||||
def disable_watchdir(self, watchdir_id):
|
||||
watchdir_id = str(watchdir_id)
|
||||
self.watchdirs[watchdir_id]['enabled'] = False
|
||||
#disable the looping call here
|
||||
self.update_timers[watchdir_id].stop()
|
||||
del self.update_timers[watchdir_id]
|
||||
self.config.save()
|
||||
component.get("EventManager").emit(AutoaddOptionsChangedEvent())
|
||||
# Disable the looping call
|
||||
if watchdir_id in self.update_timers:
|
||||
if self.update_timers[watchdir_id].running:
|
||||
self.update_timers[watchdir_id].stop()
|
||||
del self.update_timers[watchdir_id]
|
||||
# Update the config
|
||||
if self.watchdirs[watchdir_id]['enabled']:
|
||||
self.watchdirs[watchdir_id]['enabled'] = False
|
||||
self.config.save()
|
||||
component.get("EventManager").emit(AutoaddOptionsChangedEvent())
|
||||
|
||||
@export
|
||||
def set_config(self, config):
|
||||
"""sets the config dictionary"""
|
||||
"""Sets the config dictionary."""
|
||||
config = self._make_unicode(config)
|
||||
for key in config.keys():
|
||||
self.config[key] = config[key]
|
||||
self.config.save()
|
||||
@ -269,35 +285,30 @@ class Core(CorePluginBase):
|
||||
|
||||
@export
|
||||
def get_config(self):
|
||||
"""returns the config dictionary"""
|
||||
"""Returns the config dictionary."""
|
||||
return self.config.config
|
||||
|
||||
@export()
|
||||
def get_watchdirs(self):
|
||||
return self.watchdirs.keys()
|
||||
|
||||
def _clean_unicode(self, options):
|
||||
|
||||
def _make_unicode(self, options):
|
||||
opts = {}
|
||||
for key, value in options.iteritems():
|
||||
if isinstance(key, unicode):
|
||||
key = str(key)
|
||||
if isinstance(value, unicode):
|
||||
value = str(value)
|
||||
opts[key] = value
|
||||
for key in options:
|
||||
if isinstance(options[key], str):
|
||||
options[key] = unicode(options[key], "utf8")
|
||||
opts[key] = options[key]
|
||||
return opts
|
||||
|
||||
#Labels:
|
||||
|
||||
@export()
|
||||
def add(self, options={}):
|
||||
"""add a watchdir
|
||||
"""
|
||||
options = self._clean_unicode(options)
|
||||
"""Add a watch folder."""
|
||||
options = self._make_unicode(options)
|
||||
abswatchdir = os.path.abspath(options['path'])
|
||||
CheckInput(os.path.isdir(abswatchdir) , _("Path does not exist."))
|
||||
CheckInput(os.access(abswatchdir, os.R_OK|os.W_OK), "You must have read and write access to watch folder.")
|
||||
for watchdir_id, watchdir in self.watchdirs.iteritems():
|
||||
if watchdir['abspath'] == abswatchdir:
|
||||
raise Exception("Path is already being watched.")
|
||||
if abswatchdir in [wd['abspath'] for wd in self.watchdirs.itervalues()]:
|
||||
raise Exception("Path is already being watched.")
|
||||
options.setdefault('enabled', False)
|
||||
options['abspath'] = abswatchdir
|
||||
watchdir_id = self.config['next_id']
|
||||
@ -311,7 +322,7 @@ class Core(CorePluginBase):
|
||||
|
||||
@export
|
||||
def remove(self, watchdir_id):
|
||||
"""remove a label"""
|
||||
"""Remove a watch folder."""
|
||||
watchdir_id = str(watchdir_id)
|
||||
CheckInput(watchdir_id in self.watchdirs, "Unknown Watchdir: %s" % self.watchdirs)
|
||||
if self.watchdirs[watchdir_id]['enabled']:
|
||||
|
||||
@ -83,9 +83,11 @@
|
||||
<child internal-child="vbox">
|
||||
<widget class="GtkVBox" id="dialog-vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkNotebook" id="notebook1">
|
||||
<property name="visible">True</property>
|
||||
@ -94,6 +96,7 @@
|
||||
<widget class="GtkVBox" id="vbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkFrame" id="frame2">
|
||||
<property name="visible">True</property>
|
||||
@ -106,6 +109,7 @@
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox6">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox3">
|
||||
<property name="visible">True</property>
|
||||
@ -170,6 +174,89 @@
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkFrame" id="frame1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment1">
|
||||
<property name="visible">True</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment2">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox7">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkRadioButton" id="isnt_append_extension">
|
||||
<property name="label" translatable="yes">Delete .torrent after adding</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<widget class="GtkRadioButton" id="append_extension_toggle">
|
||||
<property name="label" translatable="yes">Append extension after adding:</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">isnt_append_extension</property>
|
||||
<signal name="toggled" handler="on_toggle_toggled"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkEntry" id="append_extension">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">•</property>
|
||||
<property name="text" translatable="yes">.added</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes"><b>Torrent File Action</b></property>
|
||||
<property name="use_markup">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="type">label_item</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkFrame" id="frame3">
|
||||
<property name="visible">True</property>
|
||||
@ -182,6 +269,7 @@
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox3">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkCheckButton" id="download_location_toggle">
|
||||
<property name="label" translatable="yes">Set download location</property>
|
||||
@ -244,7 +332,7 @@
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@ -259,6 +347,7 @@
|
||||
<child>
|
||||
<widget class="GtkVBox" id="vbox4">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<widget class="GtkCheckButton" id="move_completed_toggle">
|
||||
<property name="label" translatable="yes">Set move completed location</property>
|
||||
@ -336,7 +425,7 @@
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@ -392,7 +481,7 @@
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
@ -411,6 +500,7 @@
|
||||
<widget class="GtkVBox" id="vbox5">
|
||||
<property name="visible">True</property>
|
||||
<property name="border_width">6</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
@ -484,7 +574,7 @@
|
||||
<widget class="GtkSpinButton" id="max_download_speed">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">-1 -1 10000 1 10 10</property>
|
||||
<property name="adjustment">-1 -1 10000 1 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="digits">1</property>
|
||||
</widget>
|
||||
@ -498,7 +588,7 @@
|
||||
<widget class="GtkSpinButton" id="max_upload_speed">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">-1 -1 10000 1 10 10</property>
|
||||
<property name="adjustment">-1 -1 10000 1 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="digits">1</property>
|
||||
</widget>
|
||||
@ -514,7 +604,7 @@
|
||||
<widget class="GtkSpinButton" id="max_connections">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">-1 -1 10000 1 10 10</property>
|
||||
<property name="adjustment">-1 -1 10000 1 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
@ -529,7 +619,7 @@
|
||||
<widget class="GtkSpinButton" id="max_upload_slots">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">-1 -1 10000 1 10 10</property>
|
||||
<property name="adjustment">-1 -1 10000 1 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
@ -640,14 +730,16 @@
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkAlignment" id="alignment14">
|
||||
<property name="visible">True</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<widget class="GtkCheckButton" id="remove_at_ratio">
|
||||
@ -661,8 +753,8 @@
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
@ -676,8 +768,8 @@
|
||||
<signal name="toggled" handler="on_toggle_toggled"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
@ -694,8 +786,8 @@
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
@ -712,8 +804,8 @@
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="top_attach">4</property>
|
||||
<property name="bottom_attach">5</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
@ -723,21 +815,22 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="adjustment">2 0 100 0.10000000149 10 10</property>
|
||||
<property name="adjustment">2 0 100 0.10000000149 10 0</property>
|
||||
<property name="climb_rate">1</property>
|
||||
<property name="digits">1</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkHBox" id="auto_managed_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<widget class="GtkRadioButton" id="auto_managed">
|
||||
<property name="label" translatable="yes">Yes</property>
|
||||
@ -768,8 +861,8 @@
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options">GTK_FILL</property>
|
||||
</packing>
|
||||
@ -786,8 +879,8 @@
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="right_attach">3</property>
|
||||
<property name="top_attach">2</property>
|
||||
<property name="bottom_attach">3</property>
|
||||
<property name="top_attach">3</property>
|
||||
<property name="bottom_attach">4</property>
|
||||
<property name="x_options">GTK_FILL</property>
|
||||
<property name="y_options"></property>
|
||||
</packing>
|
||||
@ -805,6 +898,7 @@
|
||||
<child>
|
||||
<widget class="GtkHBox" id="add_paused_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<widget class="GtkRadioButton" id="add_paused">
|
||||
<property name="label" translatable="yes">Yes</property>
|
||||
@ -824,7 +918,6 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">add_paused</property>
|
||||
</widget>
|
||||
@ -839,10 +932,56 @@
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<widget class="GtkCheckButton" id="queue_to_top_toggle">
|
||||
<property name="label" translatable="yes">Queue to:</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_toggle_toggled"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
<widget class="GtkHBox" id="hbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<widget class="GtkRadioButton" id="queue_to_top">
|
||||
<property name="label" translatable="yes">Top</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkRadioButton" id="isnt_queue_to_top">
|
||||
<property name="label" translatable="yes">Bottom</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">queue_to_top</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="right_attach">2</property>
|
||||
<property name="top_attach">1</property>
|
||||
<property name="bottom_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
@ -911,6 +1050,7 @@
|
||||
<child>
|
||||
<widget class="GtkHButtonBox" id="hbuttonbox2">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
@ -943,7 +1083,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="opts_add_button">
|
||||
<property name="label" translatable="no">gtk-add</property>
|
||||
<property name="label">gtk-add</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
@ -959,7 +1099,7 @@
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkButton" id="opts_apply_button">
|
||||
<property name="label" translatable="no">gtk-apply</property>
|
||||
<property name="label">gtk-apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
|
||||
@ -51,11 +51,10 @@ from common import get_resource
|
||||
class OptionsDialog():
|
||||
spin_ids = ["max_download_speed", "max_upload_speed", "stop_ratio"]
|
||||
spin_int_ids = ["max_upload_slots", "max_connections"]
|
||||
chk_ids = ["stop_at_ratio", "remove_at_ratio", "move_completed", "add_paused", "auto_managed"]
|
||||
chk_ids = ["stop_at_ratio", "remove_at_ratio", "move_completed", "add_paused", "auto_managed", "queue_to_top"]
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def show(self, options={}, watchdir_id=None):
|
||||
self.glade = gtk.glade.XML(get_resource("autoadd_options.glade"))
|
||||
self.glade.signal_autoconnect({
|
||||
@ -87,6 +86,8 @@ class OptionsDialog():
|
||||
|
||||
def load_options(self, options):
|
||||
self.glade.get_widget('enabled').set_active(options.get('enabled', False))
|
||||
self.glade.get_widget('append_extension_toggle').set_active(options.get('append_extension_toggle', False))
|
||||
self.glade.get_widget('append_extension').set_text(options.get('append_extension', '.added'))
|
||||
self.glade.get_widget('download_location_toggle').set_active(options.get('download_location_toggle', False))
|
||||
self.glade.get_widget('label').set_text(options.get('label', ''))
|
||||
self.glade.get_widget('label_toggle').set_active(options.get('label_toggle', False))
|
||||
@ -97,7 +98,9 @@ class OptionsDialog():
|
||||
self.glade.get_widget(id).set_active(bool(options.get(id, True)))
|
||||
self.glade.get_widget(id+'_toggle').set_active(options.get(id+'_toggle', False))
|
||||
if not options.get('add_paused', True):
|
||||
self.glade.get_widget('isnt_add_paused').set_active(True)
|
||||
self.glade.get_widget('isnt_add_paused').set_active(True)
|
||||
if not options.get('queue_to_top', True):
|
||||
self.glade.get_widget('isnt_queue_to_top').set_active(True)
|
||||
if not options.get('auto_managed', True):
|
||||
self.glade.get_widget('isnt_auto_managed').set_active(True)
|
||||
for field in ['move_completed_path', 'path', 'download_location']:
|
||||
@ -121,9 +124,9 @@ class OptionsDialog():
|
||||
client.core.get_enabled_plugins().addCallback(on_get_enabled_plugins)
|
||||
|
||||
def set_sensitive(self):
|
||||
maintoggles = ['download_location', 'move_completed', 'label', \
|
||||
maintoggles = ['download_location', 'append_extension', 'move_completed', 'label', \
|
||||
'max_download_speed', 'max_upload_speed', 'max_connections', \
|
||||
'max_upload_slots', 'add_paused', 'auto_managed', 'stop_at_ratio']
|
||||
'max_upload_slots', 'add_paused', 'auto_managed', 'stop_at_ratio', 'queue_to_top']
|
||||
[self.on_toggle_toggled(self.glade.get_widget(x+'_toggle')) for x in maintoggles]
|
||||
|
||||
def on_toggle_toggled(self, tb):
|
||||
@ -132,6 +135,8 @@ class OptionsDialog():
|
||||
if toggle == 'download_location':
|
||||
self.glade.get_widget('download_location_chooser').set_sensitive(isactive)
|
||||
self.glade.get_widget('download_location_entry').set_sensitive(isactive)
|
||||
elif toggle == 'append_extension':
|
||||
self.glade.get_widget('append_extension').set_sensitive(isactive)
|
||||
elif toggle == 'move_completed':
|
||||
self.glade.get_widget('move_completed_path_chooser').set_sensitive(isactive)
|
||||
self.glade.get_widget('move_completed_path_entry').set_sensitive(isactive)
|
||||
@ -149,6 +154,9 @@ class OptionsDialog():
|
||||
elif toggle == 'add_paused':
|
||||
self.glade.get_widget('add_paused').set_sensitive(isactive)
|
||||
self.glade.get_widget('isnt_add_paused').set_sensitive(isactive)
|
||||
elif toggle == 'queue_to_top':
|
||||
self.glade.get_widget('queue_to_top').set_sensitive(isactive)
|
||||
self.glade.get_widget('isnt_queue_to_top').set_sensitive(isactive)
|
||||
elif toggle == 'auto_managed':
|
||||
self.glade.get_widget('auto_managed').set_sensitive(isactive)
|
||||
self.glade.get_widget('isnt_auto_managed').set_sensitive(isactive)
|
||||
@ -193,10 +201,11 @@ class OptionsDialog():
|
||||
options['path'] = self.glade.get_widget('path_entry').get_text()
|
||||
options['download_location'] = self.glade.get_widget('download_location_entry').get_text()
|
||||
options['move_completed_path'] = self.glade.get_widget('move_completed_path_entry').get_text()
|
||||
options['append_extension_toggle'] = self.glade.get_widget('append_extension_toggle').get_active()
|
||||
options['append_extension'] = self.glade.get_widget('append_extension').get_text()
|
||||
options['download_location_toggle'] = self.glade.get_widget('download_location_toggle').get_active()
|
||||
options['label'] = self.glade.get_widget('label').get_text().lower()
|
||||
options['label_toggle'] = self.glade.get_widget('label_toggle').get_active()
|
||||
|
||||
|
||||
for id in self.spin_ids:
|
||||
options[id] = self.glade.get_widget(id).get_value()
|
||||
@ -245,6 +254,7 @@ class GtkUI(GtkPluginBase):
|
||||
sw.add(self.treeView)
|
||||
sw.show_all()
|
||||
component.get("Preferences").add_page("AutoAdd", self.glade.get_widget("prefs_box"))
|
||||
self.on_show_prefs()
|
||||
|
||||
|
||||
def disable(self):
|
||||
@ -320,7 +330,6 @@ class GtkUI(GtkPluginBase):
|
||||
client.autoadd.set_options(watchdir_id, watchdir)
|
||||
|
||||
def on_show_prefs(self):
|
||||
|
||||
client.autoadd.get_config().addCallback(self.cb_get_config)
|
||||
|
||||
def on_options_changed_event(self):
|
||||
|
||||
@ -42,8 +42,8 @@ from setuptools import setup
|
||||
__plugin_name__ = "AutoAdd"
|
||||
__author__ = "Chase Sterling"
|
||||
__author_email__ = "chase.sterling@gmail.com"
|
||||
__version__ = "0.28"
|
||||
__url__ = "http://forum.deluge-torrent.org/viewtopic.php?f=9&t=26775"
|
||||
__version__ = "1.02"
|
||||
__url__ = "http://dev.deluge-torrent.org/wiki/Plugins/AutoAdd"
|
||||
__license__ = "GPLv3"
|
||||
__description__ = "Monitors folders for .torrent files."
|
||||
__long_description__ = """"""
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# core.py
|
||||
#
|
||||
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2009 John Garland <johnnybg@gmail.com>
|
||||
# Copyright (C) 2009-2010 John Garland <johnnybg+deluge@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
@ -38,6 +38,7 @@ import os
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
from wsgiref.handlers import format_date_time
|
||||
from urlparse import urljoin
|
||||
import shutil
|
||||
|
||||
from twisted.internet.task import LoopingCall
|
||||
@ -81,6 +82,7 @@ class Core(CorePluginBase):
|
||||
self.is_importing = False
|
||||
self.has_imported = False
|
||||
self.up_to_date = False
|
||||
self.need_to_resume_session = False
|
||||
self.num_blocked = 0
|
||||
self.file_progress = 0.0
|
||||
|
||||
@ -94,7 +96,7 @@ class Core(CorePluginBase):
|
||||
|
||||
update_now = False
|
||||
if self.config["load_on_start"]:
|
||||
self.pause_transfers()
|
||||
self.pause_session()
|
||||
if self.config["last_update"]:
|
||||
last_update = datetime.fromtimestamp(self.config["last_update"])
|
||||
check_period = timedelta(days=self.config["check_after_days"])
|
||||
@ -103,7 +105,8 @@ class Core(CorePluginBase):
|
||||
else:
|
||||
d = self.import_list(deluge.configmanager.get_config_dir("blocklist.cache"))
|
||||
d.addCallbacks(self.on_import_complete, self.on_import_error)
|
||||
d.addBoth(self.resume_transfers)
|
||||
if self.need_to_resume_session:
|
||||
d.addBoth(self.resume_session)
|
||||
|
||||
# This function is called every 'check_after_days' days, to download
|
||||
# and import a new list if needed.
|
||||
@ -149,7 +152,8 @@ class Core(CorePluginBase):
|
||||
else:
|
||||
d = self.import_list(self.config["url"])
|
||||
d.addCallbacks(self.on_import_complete, self.on_import_error)
|
||||
d.addBoth(self.resume_transfers)
|
||||
if self.need_to_resume_session:
|
||||
d.addBoth(self.resume_session)
|
||||
|
||||
return d
|
||||
|
||||
@ -281,7 +285,7 @@ class Core(CorePluginBase):
|
||||
d = f
|
||||
if f.check(error.PageRedirect):
|
||||
# Handle redirect errors
|
||||
location = error_msg.split(" to ")[1]
|
||||
location = urljoin(self.config["url"], error_msg.split(" to ")[1])
|
||||
if "Moved Permanently" in error_msg:
|
||||
log.debug("Setting blocklist url to %s", location)
|
||||
self.config["url"] = location
|
||||
@ -417,13 +421,14 @@ class Core(CorePluginBase):
|
||||
else:
|
||||
self.reader = create_reader(self.config["list_type"], self.config["list_compression"])
|
||||
|
||||
def pause_transfers(self):
|
||||
self.session_was_paused = self.core.session.is_paused()
|
||||
if not self.session_was_paused:
|
||||
def pause_session(self):
|
||||
if not self.core.session.is_paused():
|
||||
self.core.session.pause()
|
||||
self.need_to_resume_session = True
|
||||
else:
|
||||
self.need_to_resume_session = False
|
||||
|
||||
def resume_transfers(self, result):
|
||||
if not self.session_was_paused:
|
||||
self.session_was_paused = True
|
||||
self.core.session.resume()
|
||||
def resume_session(self, result):
|
||||
self.core.session.resume()
|
||||
self.need_to_resume_session = False
|
||||
return result
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#
|
||||
# decompressers.py
|
||||
#
|
||||
# Copyright (C) 2009 John Garland <johnnybg@gmail.com>
|
||||
# Copyright (C) 2009-2010 John Garland <johnnybg+deluge@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user