Compare commits
259 Commits
deluge-1.3
...
deluge-1.3
Author | SHA1 | Date | |
---|---|---|---|
cc56764ee9 | |||
53f485d87e | |||
f95cfb42c3 | |||
26f5be1760 | |||
d3f47097c1 | |||
7a2092d3c4 | |||
3e632600c6 | |||
4df58b51ff | |||
4fc8032c88 | |||
5e1874eb8d | |||
810391316c | |||
47daef1e47 | |||
294ad9fae1 | |||
f1fe593fd6 | |||
c982e8de67 | |||
33339b1dc6 | |||
ecf5af1e16 | |||
4e77c46694 | |||
3f8526160d | |||
82fb3408ee | |||
223c9319c7 | |||
c61c2d8c3a | |||
8bdf1e9044 | |||
98dcc3f26e | |||
e2b0ceae1d | |||
5dba838533 | |||
54eb28a097 | |||
4bd4c78969 | |||
1990bdcb52 | |||
70bf274974 | |||
a4fb8e769b | |||
56bbc90c5b | |||
2c54c696a1 | |||
ddde10ec99 | |||
fed5221503 | |||
c0650f88d1 | |||
5d5edd2639 | |||
f77440efcb | |||
74aa924625 | |||
0338fc6f2d | |||
12118c5454 | |||
1bdb072ac8 | |||
ade26794ec | |||
8c4a08bb87 | |||
34650f4b3c | |||
59fa974b3b | |||
98e8418087 | |||
176064b520 | |||
c5ad5589df | |||
9987d335a0 | |||
d6b44afb99 | |||
7597ba9343 | |||
41f1ad9f5f | |||
29d3e72f49 | |||
d04af1e392 | |||
c620ddcba0 | |||
e8aee7327b | |||
018330c92c | |||
58c048f1b0 | |||
9f75d4597e | |||
ffc48d3810 | |||
c38b00dd07 | |||
479c96745c | |||
63807899a0 | |||
a3806b6d7a | |||
7bd87d1a82 | |||
8bf18d6694 | |||
06ee112344 | |||
3cc43f63a0 | |||
27bfa5a649 | |||
5057e2caab | |||
686fb31844 | |||
03d5654a16 | |||
83f0d72601 | |||
19093e03ae | |||
d28de71995 | |||
f107485871 | |||
984691f74c | |||
8ba8e24fab | |||
7c9433fd8b | |||
821f395d8b | |||
e9ba3972ad | |||
7fbe163c24 | |||
e4118048eb | |||
bb67721002 | |||
9856f97323 | |||
e2e37a83de | |||
61c125420b | |||
36a78d8f21 | |||
3f4cfd5c88 | |||
cc85f00588 | |||
72b58d19c1 | |||
bbf17772ac | |||
2cbcb35c9e | |||
7ef7cc41b6 | |||
7c6d1f30ff | |||
8911600155 | |||
c58221c866 | |||
92eb9feae4 | |||
b9311d4b57 | |||
b0d78da13b | |||
12365241a5 | |||
8ba5e78610 | |||
1bfaf74ff9 | |||
e362b36a59 | |||
a0ae3ebfce | |||
08d7b9fba3 | |||
95ce85ec78 | |||
93dee4c764 | |||
dcd85e64c0 | |||
58e8a0e562 | |||
bfa3564ba3 | |||
669d10e6be | |||
3d1b47bca6 | |||
2fde78f236 | |||
444740709a | |||
d7d91aec0b | |||
e865180e83 | |||
782b39c90d | |||
7e71995e55 | |||
1dd078f4f1 | |||
0045ec0cf1 | |||
5ecc580463 | |||
70047c367a | |||
b65918d616 | |||
3ca886ac7a | |||
74208c27f8 | |||
7cc8243849 | |||
c741e127b9 | |||
b5ea33e506 | |||
ffe0c168bb | |||
b8c345ff26 | |||
9286d43ba8 | |||
23b0f58eb4 | |||
ccd2e5e41d | |||
62a1602d5a | |||
b50c848054 | |||
e4d94ea528 | |||
fc4ca861da | |||
74d59eb6ec | |||
ce4b4ef48e | |||
2e0b0d9474 | |||
0e4101a9a3 | |||
ba4ca111c6 | |||
827b53792b | |||
6e75a194c3 | |||
a3a9cae9f7 | |||
f447bd5cea | |||
f79785abf2 | |||
a2423ba536 | |||
4307699bbf | |||
e7a2a8dcee | |||
67450f43b3 | |||
10ccd9aaaf | |||
16568404e7 | |||
901f5a84bf | |||
8ffa80c2a2 | |||
0f16faabcb | |||
508b5aed8f | |||
f2b2f98fac | |||
42e33d452e | |||
ea75cfb6d7 | |||
9155191b0a | |||
0571799a07 | |||
aaf4aa5226 | |||
3e98ef5f89 | |||
c398ef57a5 | |||
68b413af5f | |||
3f79d36489 | |||
582c6735d0 | |||
5d43c2ac94 | |||
845b95d88f | |||
66437531e8 | |||
666bb09513 | |||
7d88cb1850 | |||
d825be4901 | |||
135c98634a | |||
ecb457c043 | |||
cd8ab2805c | |||
b7ca968fae | |||
8f71b8d5c6 | |||
8a72187998 | |||
d9f1210fe7 | |||
0a9fe242e7 | |||
e53413902c | |||
bd500c30de | |||
b8b27bdaf2 | |||
ad8b757387 | |||
fd8dc457fa | |||
69509f446e | |||
db502253c9 | |||
8d25a5e691 | |||
3c85cc4b03 | |||
03abe32054 | |||
7695c4e566 | |||
a64fe05890 | |||
762ad783e2 | |||
aa969fd830 | |||
e5f56e2fdd | |||
610cd7dd33 | |||
bb7b529c29 | |||
43390b850a | |||
8c2189f161 | |||
37ea2854a2 | |||
7b9bca0957 | |||
ec3d698a14 | |||
3309f59333 | |||
68990b4a3f | |||
17cac01673 | |||
18eb885983 | |||
cb661f2595 | |||
fc14b00868 | |||
725d06f439 | |||
3569e18712 | |||
42118932f2 | |||
c480132fa2 | |||
313a04f9a6 | |||
d1f2776463 | |||
dac524ccf3 | |||
bafd0209af | |||
0a6c2ee147 | |||
85cdbec10d | |||
f083a3b67b | |||
d08ea7da81 | |||
483c439e38 | |||
914413c88f | |||
438dff177c | |||
196458399e | |||
1d243d5967 | |||
418037dd43 | |||
900907a545 | |||
b3a721b539 | |||
d783b8ead7 | |||
5572f61022 | |||
fe6e9ec467 | |||
675a64e213 | |||
931b8aec71 | |||
f19caf69e0 | |||
56a24f16f2 | |||
275c939b95 | |||
8238c63156 | |||
c19718b66a | |||
eac52dec73 | |||
124daaf8b2 | |||
5d6fa23011 | |||
0da64f7db4 | |||
b9030dfb8b | |||
3528549430 | |||
39a04aae20 | |||
72a2ace0a1 | |||
4240345daf | |||
cb9867a26f | |||
07e166b94a | |||
3c7f492451 | |||
aeca5dd1e7 | |||
c194f6bbe4 | |||
4d77241603 | |||
a2a45f1a0b | |||
217087a2fe |
22
.gitattributes
vendored
22
.gitattributes
vendored
@ -1,14 +1,24 @@
|
||||
/libtorrent export-ignore
|
||||
/win32 export-ignore
|
||||
docs/build export-ignore
|
||||
docs/source export-ignore
|
||||
/tests export-ignore
|
||||
deluge/scripts export-ignore
|
||||
/libtorrent/ export-ignore
|
||||
/win32/ export-ignore
|
||||
/osx/ export-ignore
|
||||
docs/build/ export-ignore
|
||||
docs/source/ export-ignore
|
||||
/tests/ export-ignore
|
||||
deluge/scripts/ export-ignore
|
||||
setup.cfg export-ignore
|
||||
check_glade.sh export-ignore
|
||||
createicons.sh export-ignore
|
||||
create_potfiles_in.py export-ignore
|
||||
gettextize.sh export-ignore
|
||||
deluge/i18n/deluge.pot export-ignore
|
||||
deluge/ui/web/css/*-debug.css export-ignore
|
||||
deluge/ui/web/js/*-debug.js export-ignore
|
||||
deluge/ui/web/js/deluge-all/ export-ignore
|
||||
deluge/ui/web/js/ext-extensions/ export-ignore
|
||||
deluge/ui/web/gen_gettext.py export-ignore
|
||||
deluge/ui/web/build export-ignore
|
||||
deluge/ui/web/docs/ export-ignore
|
||||
|
||||
.gitattributes export-ignore
|
||||
.gitmodules export-ignore
|
||||
.gitignore export-ignore
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,3 +9,5 @@ dist
|
||||
_trial_temp
|
||||
deluge/i18n/*/
|
||||
*.desktop
|
||||
.build_data*
|
||||
osx/app
|
||||
|
195
ChangeLog
195
ChangeLog
@ -1,3 +1,198 @@
|
||||
=== Deluge 1.3.10 (15 October 2014) ===
|
||||
==== GtkUI ====
|
||||
* #2256: Indexes are not updated correctly when removing column
|
||||
* Fix bug in the torrentview when Plugins added a column
|
||||
|
||||
==== WebUI ====
|
||||
* Security update for POODLE vulnerability
|
||||
|
||||
=== Deluge 1.3.9 (4 October 2014) ===
|
||||
==== GtkUI ====
|
||||
* #2514: Fix every torrent is displayed twice in classic mode
|
||||
|
||||
=== Deluge 1.3.8 (4 October 2014) ===
|
||||
==== Core ====
|
||||
* #1126 & #2322: Emit FinishedEvent after moving storage complete
|
||||
* Fixes to mitigate fastresume corruption
|
||||
|
||||
==== GtkUI ====
|
||||
* #2335: Fix application startup failing with 'cannot acquire lock' error
|
||||
* #2497: Fix the Queued Torrents 'Clear' button not properly clearing the list of torrent
|
||||
* #2496: Fix updating core_config before setting default options
|
||||
* #2493: Fix TypeError if active workspace is None
|
||||
* LP:#1168858: Nautilus window opens behind current window
|
||||
* Fix showing the open_folder menuitem
|
||||
* Suppress unimportant gnome warnings
|
||||
* Optimized the updating of the torrent view
|
||||
* Fixed Indicator icon label issue
|
||||
* Fix listview error with new config
|
||||
|
||||
==== WebUI ====
|
||||
* Ensure values are updated from config upon showing a plugin page
|
||||
|
||||
==== Extractor ====
|
||||
* Add WebUI plugin page
|
||||
* Find 7-zip application path on Windows via registry
|
||||
|
||||
==== Execute ====
|
||||
* #1290: Add a TorrentRemoved event option
|
||||
|
||||
==== Scheduler ====
|
||||
* #2238: Fix an 'undefined this.scheduleCells' error in javascript console
|
||||
|
||||
==== Notifications ====
|
||||
* #1310: Add WebUI plugin page
|
||||
|
||||
==== Blocklist ====
|
||||
* #2478: Add WebUI plugin page
|
||||
|
||||
==== Console ====
|
||||
* #2470: Fix console parsing args
|
||||
|
||||
=== Deluge 1.3.7 (9 July 2014) ===
|
||||
==== Core ====
|
||||
* #2324: Encryption level set by Deluge did not match libtorrent values
|
||||
* #2303: Torrent state was not updated until after emitting TorrentFinishedEvent
|
||||
* Fix twisted 13.1 compatability
|
||||
* #2343: Fix error if listen interface is whitespace
|
||||
* #2082: Validate ip address for listen_interface entry
|
||||
* #1490: Increase the Alertmanager interval to 0.3s
|
||||
* Prevent private flagged torrents auto-merging trackers
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix issue with Plugins that add Tab to torrentdetails
|
||||
* Fix the scalable icon install directory
|
||||
* #2335: Fix IPC lockfile issue preventing start of deluge-gtk
|
||||
* #2365: Fix hiding Progress column generating TypeError
|
||||
* #2371: Add StartupWMClass to desktop file
|
||||
* #2372: Fix the Ratio column not retaining position
|
||||
* #2369: Fix bypassing the password dialog when showing/quitting
|
||||
|
||||
==== WebUI ====
|
||||
* #2374: Fix right-click selection issue
|
||||
* #2310: Fix unicode password support
|
||||
* #2418: Fix WebUI error when adding non-ascii torrent
|
||||
|
||||
==== Windows OS ====
|
||||
* Allow silent uninstall for Windows package
|
||||
* #2367: Fix DelugeStart theme not showing Private Flag as ticked/checked
|
||||
* #2315: Potential fix for lost window issue
|
||||
|
||||
==== Extractor ====
|
||||
* #2290: Fix dotted filenames being rejected
|
||||
|
||||
=== Deluge 1.3.6 (25 Feburary 2013) ===
|
||||
==== Core ====
|
||||
* Catch & log KeyError when removing a torrent from the queued torrents set
|
||||
* Fix moving/renaming torrents issues when using libtorrent 0.16
|
||||
* Make sure queue order is preserved when restarting
|
||||
* #2160: Disable use of python bindings for libtorrent extensions and replace with session flag
|
||||
* #2163: Fix unable add torrent file with empty (0:) encoding tag
|
||||
* #2201: Fix error in authmanager if auth file has extra newlines
|
||||
* #2109: Fix the Proxy settings not being cleared by setting None
|
||||
* #2110: Fix accepting magnet uris with xt param anywhere within them
|
||||
* #2204: Fix daemon shutdown hang with large numbers of torrents
|
||||
* Fix prioritize first/last pieces option for magnet links
|
||||
|
||||
==== Client ====
|
||||
* Fix keyerrors after removing torrents from UIs
|
||||
|
||||
==== GtkUI ====
|
||||
* Add move completed option to add torrent dialog
|
||||
* Prevent jitter in torrent view
|
||||
* Fix torrent creation with non-ascii characters
|
||||
* Fix #2100 : Add option not to bring main window to front when adding torrents through ipcinterface
|
||||
* Add Quit Dialog when toggling classic mode in preferences and only show connection manager when not in classic mode.
|
||||
* #2169: Fix 'Download Location' in the Add Torrent Dialog not set correctly when folder typed into Other->Location field
|
||||
* #2171: Fix the Add Peer dialog not responding if empty or invalid values entered
|
||||
* #2104: Fix no title set for the appindicator
|
||||
* #2086: Fix submenus and icons for appindicator
|
||||
* #2146: Fix missing translations in View|Tabs submenu
|
||||
* Fix torrent names on libtorrent 0.16 on windows
|
||||
* #2147: Fix missing translations for plugin preferences page
|
||||
* #1474: Fix the on_show_prefs hook not being executed immediatly after enabling a plugin
|
||||
* #1946: Fix ReactorNotRestartable error when set as startup application
|
||||
* #2130: Fix same name can be given to different files in Add Torrent dialog
|
||||
* #2129: Fix empty filename able to be set in AddTorrent dialog
|
||||
* #2228: Fix Apply-To-All in AddTorrent Dialog copying file renames to other torrents
|
||||
* #2260: Fix the Add Torrent dialog also bringing the main window to active workspace
|
||||
* Fix showing exception error to user in Classic Mode with no libtorrent installed
|
||||
|
||||
==== Console ====
|
||||
* LP#1004793: Enable use of connect command in non-interactive mode
|
||||
* Ensure console commands are executed in order
|
||||
* #2065: Fix crash with missing closing quote
|
||||
* #1397: Add support for -s STATE in info command
|
||||
|
||||
==== WebUI ====
|
||||
* Add move completed option to add torrent dialog
|
||||
* #2112: Fix world readable tmp directory in json_api
|
||||
* #2069: Fix login window layout problem when using larger than default font size
|
||||
* #1890: Fix columns in files and peers view could use some spacing
|
||||
* #2103: Fix sorting by name is case-sensitive [sedulous]
|
||||
* #2120: Fix manually entered values not being saved in spinners
|
||||
* #2212: Fix unable to scroll in proxy preferences page
|
||||
* Fix autoconnecting to the default host
|
||||
* #2046: Fix plugins not enabling properly until after refreshing page
|
||||
* #2125: Fix plugin methods not being available when enabled until restart
|
||||
* #2085: Fix not showing torrents in sidebar for categories other than 'All' in classic mode
|
||||
* #2232: Fix flag icon path in Peers Tab missing deluge.config.base
|
||||
* Fix submenus closing upon mouse click
|
||||
* Add failed login log message, including IP address, to enable use with fail2ban
|
||||
* #2261: Fix Proxy settings not being saved in preferences
|
||||
|
||||
==== Windows OS ====
|
||||
* Hide the cmd windows when running deluged.exe or deluge-web.exe
|
||||
* Add deluged-debug.exe and deluge-web-debug.exe that still show the cmd window
|
||||
* Add gtk locale files to fix untranslated text
|
||||
* Fix the Open Folder option not working with non-ascii paths
|
||||
* Fix the daemon starting with config dir containing spaces
|
||||
* Fix Windows tray submenu items requiring right-click instead of left-click
|
||||
* Fix issue with adding some torrents with illegal characters via url in gtk client
|
||||
* #2240: Fix freespace issue with large capacity drives
|
||||
|
||||
==== OS X ====
|
||||
* Fix Open File/Folder option
|
||||
* Add OS X Menu for GTK Quartz
|
||||
|
||||
==== Execute ====
|
||||
* Fix execute plugin not working with unicode torrent names
|
||||
|
||||
==== Extractor ====
|
||||
* Add Windows support, using 7-zip
|
||||
* Added support for more extensions
|
||||
* Disabled extracting 'Move Completed' torrents due to race condition
|
||||
|
||||
=== Deluge 1.3.5 (09 April 2012) ===
|
||||
==== Core ====
|
||||
* Fix not properly detecting when torrent is at end of queue
|
||||
* #2049: Preserve order when moving multiple torrents in the queue
|
||||
|
||||
==== GtkUI ====
|
||||
* Modified fix for #1957, keyerror with non-acsii columns
|
||||
* Fix translation of items in Sidebar and Torrent Menu
|
||||
* #2052: Fix translation of Progress bar text
|
||||
* #2071: Fix KeyError in gtkui when file priority set to value '3'
|
||||
* #2064: Fix files treeview height in Create Dialog
|
||||
* Fix missing semi-colon in deluge.desktop
|
||||
* Disable setting file priorities for seeding torrents
|
||||
* Bring MainWindow to front when opening another instance
|
||||
|
||||
==== WebUI ====
|
||||
* #2050: Fix 'Up Speed' column not sorting
|
||||
* Hide unused Infohash button in WebUI
|
||||
|
||||
==== Label ====
|
||||
* Disable unusable items for 'All' in sidebar menu
|
||||
* Fix items for translation
|
||||
|
||||
==== Console ====
|
||||
* Fix prefixed space for tab completing commands
|
||||
* Fix missing trailing space for command options with tab complete
|
||||
|
||||
==== Blocklist ====
|
||||
* Use (documented) formatdate over format_date_time
|
||||
|
||||
=== Deluge 1.3.4 (03 March 2012) ===
|
||||
==== Core ====
|
||||
* #1921: Free disk space reporting incorrectly in FreeBSD
|
||||
|
8
DEPENDS
8
DEPENDS
@ -12,19 +12,19 @@
|
||||
* geoip-database (optional)
|
||||
* setproctitle (optional)
|
||||
|
||||
* libtorrent >= 0.14, or build the included version
|
||||
* libtorrent (rasterbar) >= 0.14
|
||||
|
||||
* If building included libtorrent::
|
||||
* If building libtorrent:
|
||||
* boost >= 1.34.1
|
||||
* openssl
|
||||
* zlib
|
||||
|
||||
=== Gtk ===
|
||||
* python-notify (libnotify python wrapper)
|
||||
* pygame
|
||||
* pygtk >= 2.12
|
||||
* librsvg
|
||||
* xdg-utils
|
||||
* python-notify (optional)
|
||||
* pygame (optional)
|
||||
|
||||
=== Web ===
|
||||
* mako
|
||||
|
@ -47,7 +47,7 @@ supports.
|
||||
|
||||
REQUIRED_VERSION = "0.14.9.0"
|
||||
|
||||
def check_version(LT):
|
||||
def check_version(lt):
|
||||
from deluge.common import VersionSplit
|
||||
if VersionSplit(lt.version) < VersionSplit(REQUIRED_VERSION):
|
||||
raise ImportError("This version of Deluge requires libtorrent >=%s!" % REQUIRED_VERSION)
|
||||
|
131
deluge/common.py
131
deluge/common.py
@ -42,12 +42,18 @@ import subprocess
|
||||
import platform
|
||||
import sys
|
||||
import chardet
|
||||
import pkg_resources
|
||||
import gettext
|
||||
import locale
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
from deluge.error import *
|
||||
from deluge.log import LOG as log
|
||||
|
||||
# Do a little hack here just in case the user has json-py installed since it
|
||||
# has a different api
|
||||
if not hasattr(json, "dumps"):
|
||||
@ -63,10 +69,6 @@ if not hasattr(json, "dumps"):
|
||||
json.dump = dump
|
||||
json.load = load
|
||||
|
||||
import pkg_resources
|
||||
import gettext
|
||||
import locale
|
||||
|
||||
# Initialize gettext
|
||||
try:
|
||||
if hasattr(locale, "bindtextdomain"):
|
||||
@ -75,14 +77,11 @@ try:
|
||||
locale.textdomain("deluge")
|
||||
gettext.install("deluge", pkg_resources.resource_filename("deluge", "i18n"), unicode=True)
|
||||
except Exception, e:
|
||||
from deluge.log import LOG as log
|
||||
log.error("Unable to initialize gettext/locale!")
|
||||
log.exception(e)
|
||||
import __builtin__
|
||||
__builtin__.__dict__["_"] = lambda x: x
|
||||
|
||||
from deluge.error import *
|
||||
|
||||
LT_TORRENT_STATE = {
|
||||
"Queued": 0,
|
||||
"Checking": 1,
|
||||
@ -102,7 +101,6 @@ LT_TORRENT_STATE = {
|
||||
7: "Checking Resume Data"
|
||||
}
|
||||
|
||||
|
||||
TORRENT_STATE = [
|
||||
"Allocating",
|
||||
"Checking",
|
||||
@ -117,7 +115,10 @@ FILE_PRIORITY = {
|
||||
0: "Do Not Download",
|
||||
1: "Normal Priority",
|
||||
2: "High Priority",
|
||||
3: "High Priority",
|
||||
4: "High Priority",
|
||||
5: "High Priority",
|
||||
6: "High Priority",
|
||||
7: "Highest Priority",
|
||||
"Do Not Download": 0,
|
||||
"Normal Priority": 1,
|
||||
@ -156,11 +157,15 @@ def get_default_config_dir(filename=None):
|
||||
else:
|
||||
return os.path.join(appDataPath, "deluge")
|
||||
else:
|
||||
import xdg.BaseDirectory
|
||||
if filename:
|
||||
return os.path.join(xdg.BaseDirectory.save_config_path("deluge"), filename)
|
||||
else:
|
||||
return xdg.BaseDirectory.save_config_path("deluge")
|
||||
from xdg.BaseDirectory import save_config_path
|
||||
try:
|
||||
if filename:
|
||||
return os.path.join(save_config_path("deluge"), filename)
|
||||
else:
|
||||
return save_config_path("deluge")
|
||||
except OSError, e:
|
||||
log.error("Unable to use default config directory, exiting... (%s)", e)
|
||||
sys.exit(1)
|
||||
|
||||
def get_default_download_dir():
|
||||
"""
|
||||
@ -168,22 +173,20 @@ def get_default_download_dir():
|
||||
:rtype: string
|
||||
|
||||
"""
|
||||
if windows_check():
|
||||
return os.path.join(os.path.expanduser("~"), 'Downloads')
|
||||
else:
|
||||
download_dir = ""
|
||||
if not windows_check():
|
||||
from xdg.BaseDirectory import xdg_config_home
|
||||
userdir_file = os.path.join(xdg_config_home, 'user-dirs.dirs')
|
||||
try:
|
||||
for line in open(userdir_file, 'r'):
|
||||
if not line.startswith('#') and 'XDG_DOWNLOAD_DIR' in line:
|
||||
download_dir = os.path.expandvars(\
|
||||
line.partition("=")[2].rstrip().strip('"'))
|
||||
if os.path.isdir(download_dir):
|
||||
return download_dir
|
||||
for line in open(os.path.join(xdg_config_home, 'user-dirs.dirs'), 'r'):
|
||||
if not line.startswith('#') and line.startswith('XDG_DOWNLOAD_DIR'):
|
||||
download_dir = os.path.expandvars(line.partition("=")[2].rstrip().strip('"'))
|
||||
break
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
return os.environ.get("HOME")
|
||||
if not download_dir:
|
||||
download_dir = os.path.join(os.path.expanduser("~"), 'Downloads')
|
||||
return download_dir
|
||||
|
||||
def windows_check():
|
||||
"""
|
||||
@ -228,18 +231,27 @@ def get_pixmap(fname):
|
||||
return pkg_resources.resource_filename("deluge", os.path.join("data", \
|
||||
"pixmaps", fname))
|
||||
|
||||
def open_file(path):
|
||||
def open_file(path, timestamp=None):
|
||||
"""
|
||||
Opens a file or folder using the system configured program
|
||||
|
||||
:param path: the path to the file or folder to open
|
||||
:type path: string
|
||||
:param timestamp: the timestamp of the event that requested to open
|
||||
:type timestamp: int
|
||||
|
||||
"""
|
||||
if windows_check():
|
||||
os.startfile("%s" % path)
|
||||
os.startfile(path.decode("utf8"))
|
||||
elif osx_check():
|
||||
subprocess.Popen(["open", "%s" % path])
|
||||
else:
|
||||
subprocess.Popen(["xdg-open", "%s" % path])
|
||||
if timestamp is None:
|
||||
timestamp = int(time.time())
|
||||
env = os.environ.copy()
|
||||
env["DESKTOP_STARTUP_ID"] = "%s-%u-%s-xdg_open_TIME%d" % \
|
||||
(os.path.basename(sys.argv[0]), os.getpid(), os.uname()[1], timestamp)
|
||||
subprocess.Popen(["xdg-open", "%s" % path], env=env)
|
||||
|
||||
def open_url_in_browser(url):
|
||||
"""
|
||||
@ -452,7 +464,9 @@ def is_magnet(uri):
|
||||
True
|
||||
|
||||
"""
|
||||
if uri[:20] == "magnet:?xt=urn:btih:":
|
||||
magnet_scheme = 'magnet:?'
|
||||
xt_param = 'xt=urn:btih:'
|
||||
if uri.startswith(magnet_scheme) and xt_param in uri:
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -516,13 +530,12 @@ def free_space(path):
|
||||
:raises InvalidPathError: if the path is not valid
|
||||
|
||||
"""
|
||||
if not os.path.exists(path):
|
||||
if not path or not os.path.exists(path):
|
||||
raise InvalidPathError("%s is not a valid path" % path)
|
||||
|
||||
if windows_check():
|
||||
import win32file
|
||||
sectors, bytes, free, total = map(long, win32file.GetDiskFreeSpace(path))
|
||||
return (free * sectors * bytes)
|
||||
from win32file import GetDiskFreeSpaceEx
|
||||
return GetDiskFreeSpaceEx(path)[0]
|
||||
else:
|
||||
disk_data = os.statvfs(path.encode("utf8"))
|
||||
block_size = disk_data.f_frsize
|
||||
@ -546,15 +559,23 @@ def is_ip(ip):
|
||||
import socket
|
||||
#first we test ipv4
|
||||
try:
|
||||
if socket.inet_pton(socket.AF_INET, "%s" % (ip)):
|
||||
return True
|
||||
if windows_check():
|
||||
if socket.inet_aton("%s" % (ip)):
|
||||
return True
|
||||
else:
|
||||
if socket.inet_pton(socket.AF_INET, "%s" % (ip)):
|
||||
return True
|
||||
except socket.error:
|
||||
if not socket.has_ipv6:
|
||||
return False
|
||||
#now test ipv6
|
||||
try:
|
||||
if socket.inet_pton(socket.AF_INET6, "%s" % (ip)):
|
||||
if windows_check():
|
||||
log.warning("ipv6 check unavailable on windows")
|
||||
return True
|
||||
else:
|
||||
if socket.inet_pton(socket.AF_INET6, "%s" % (ip)):
|
||||
return True
|
||||
except socket.error:
|
||||
return False
|
||||
|
||||
@ -612,37 +633,55 @@ def xml_encode(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.
|
||||
Decodes a string and return unicode. If it cannot decode using
|
||||
`:param:encoding` then it will try latin1, and if that fails,
|
||||
try to detect the string encoding. If that fails, decode with
|
||||
ignore.
|
||||
|
||||
:param s: string to decode
|
||||
:type s: string
|
||||
:keyword encoding: the encoding to use in the decoding
|
||||
:type encoding: string
|
||||
:returns: s converted to unicode
|
||||
:rtype: unicode
|
||||
|
||||
"""
|
||||
if not s:
|
||||
return u''
|
||||
elif isinstance(s, unicode):
|
||||
return s
|
||||
|
||||
try:
|
||||
s = s.decode(encoding).encode("utf8", "ignore")
|
||||
except UnicodeDecodeError:
|
||||
s = s.decode(chardet.detect(s)["encoding"], "ignore").encode("utf8", "ignore")
|
||||
return s
|
||||
encodings = [lambda: ("utf8", 'strict'),
|
||||
lambda: ("iso-8859-1", 'strict'),
|
||||
lambda: (chardet.detect(s)["encoding"], 'strict'),
|
||||
lambda: (encoding, 'ignore')]
|
||||
|
||||
def utf8_encoded(s):
|
||||
if not encoding is "utf8":
|
||||
encodings.insert(0, lambda: (encoding, 'strict'))
|
||||
|
||||
for l in encodings:
|
||||
try:
|
||||
return s.decode(*l())
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
return u''
|
||||
|
||||
def utf8_encoded(s, encoding="utf8"):
|
||||
"""
|
||||
Returns a utf8 encoded string of s
|
||||
|
||||
:param s: (unicode) string to (re-)encode
|
||||
:type s: basestring
|
||||
:keyword encoding: the encoding to use in the decoding
|
||||
:type encoding: string
|
||||
:returns: a utf8 encoded string of s
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
if isinstance(s, str):
|
||||
s = decode_string(s)
|
||||
s = decode_string(s, encoding).encode("utf8")
|
||||
elif isinstance(s, unicode):
|
||||
s = s.encode("utf8", "ignore")
|
||||
s = s.encode("utf8")
|
||||
return s
|
||||
|
||||
class VersionSplit(object):
|
||||
|
@ -51,7 +51,7 @@ from deluge.log import LOG as log
|
||||
class AlertManager(component.Component):
|
||||
def __init__(self):
|
||||
log.debug("AlertManager initialized..")
|
||||
component.Component.__init__(self, "AlertManager", interval=0.05)
|
||||
component.Component.__init__(self, "AlertManager", interval=0.3)
|
||||
self.session = component.get("Core").session
|
||||
|
||||
self.session.set_alert_mask(
|
||||
|
@ -121,10 +121,10 @@ class AuthManager(component.Component):
|
||||
f = open(auth_file, "r").readlines()
|
||||
|
||||
for line in f:
|
||||
if line.startswith("#"):
|
||||
# This is a comment line
|
||||
continue
|
||||
line = line.strip()
|
||||
if line.startswith("#") or not line:
|
||||
# This line is a comment or empty
|
||||
continue
|
||||
try:
|
||||
lsplit = line.split(":")
|
||||
except Exception, e:
|
||||
|
@ -84,7 +84,10 @@ class Core(component.Component):
|
||||
while len(version) < 4:
|
||||
version.append(0)
|
||||
|
||||
self.session = lt.session(lt.fingerprint("DE", *version), flags=0)
|
||||
# Note: All libtorrent python bindings to set plugins/extensions need to be disabled
|
||||
# due to GIL issue. https://code.google.com/p/libtorrent/issues/detail?id=369
|
||||
# Setting session flags to 1 enables all libtorrent default plugins
|
||||
self.session = lt.session(lt.fingerprint("DE", *version), flags=1)
|
||||
|
||||
# Load the session state if available
|
||||
self.__load_session_state()
|
||||
@ -92,6 +95,8 @@ class Core(component.Component):
|
||||
# Set the user agent
|
||||
self.settings = lt.session_settings()
|
||||
self.settings.user_agent = "Deluge %s" % deluge.common.get_version()
|
||||
# Increase the alert queue size so that alerts don't get lost
|
||||
self.settings.alert_queue_size = 10000
|
||||
|
||||
# Set session settings
|
||||
self.settings.send_redundant_have = True
|
||||
@ -103,9 +108,11 @@ class Core(component.Component):
|
||||
self.session.set_settings(self.settings)
|
||||
|
||||
# Load metadata extension
|
||||
self.session.add_extension(lt.create_metadata_plugin)
|
||||
self.session.add_extension(lt.create_ut_metadata_plugin)
|
||||
self.session.add_extension(lt.create_smart_ban_plugin)
|
||||
# Note: All libtorrent python bindings to set plugins/extensions need to be disabled
|
||||
# due to GIL issue. https://code.google.com/p/libtorrent/issues/detail?id=369
|
||||
# self.session.add_extension(lt.create_metadata_plugin)
|
||||
# self.session.add_extension(lt.create_ut_metadata_plugin)
|
||||
# self.session.add_extension(lt.create_smart_ban_plugin)
|
||||
|
||||
# Create the components
|
||||
self.eventmanager = EventManager()
|
||||
@ -127,8 +134,11 @@ class Core(component.Component):
|
||||
# store the one in the config so we can restore it on shutdown
|
||||
self.__old_interface = None
|
||||
if listen_interface:
|
||||
self.__old_interface = self.config["listen_interface"]
|
||||
self.config["listen_interface"] = listen_interface
|
||||
if deluge.common.is_ip(listen_interface):
|
||||
self.__old_interface = self.config["listen_interface"]
|
||||
self.config["listen_interface"] = listen_interface
|
||||
else:
|
||||
log.error("Invalid listen interface (must be IP Address): %s", listen_interface)
|
||||
|
||||
def start(self):
|
||||
"""Starts the core"""
|
||||
@ -270,7 +280,7 @@ class Core(component.Component):
|
||||
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("Error occurred downloading torrent from %s", url)
|
||||
log.error("Reason: %s", failure.getErrorMessage())
|
||||
result = failure
|
||||
return result
|
||||
@ -414,7 +424,11 @@ class Core(component.Component):
|
||||
@export
|
||||
def get_torrent_status(self, torrent_id, keys, diff=False):
|
||||
# Build the status dictionary
|
||||
status = self.torrentmanager[torrent_id].get_status(keys, diff)
|
||||
try:
|
||||
status = self.torrentmanager[torrent_id].get_status(keys, diff)
|
||||
except KeyError:
|
||||
# Torrent was probaly removed meanwhile
|
||||
return {}
|
||||
|
||||
# Get the leftover fields and ask the plugin manager to fill them
|
||||
leftover_fields = list(set(keys) - set(status.keys()))
|
||||
@ -706,7 +720,8 @@ class Core(component.Component):
|
||||
@export
|
||||
def queue_top(self, torrent_ids):
|
||||
log.debug("Attempting to queue %s to top", torrent_ids)
|
||||
for torrent_id in torrent_ids:
|
||||
# torrent_ids must be sorted in reverse before moving to preserve order
|
||||
for torrent_id in sorted(torrent_ids, key=self.torrentmanager.get_queue_position, reverse=True):
|
||||
try:
|
||||
# If the queue method returns True, then we should emit a signal
|
||||
if self.torrentmanager.queue_top(torrent_id):
|
||||
@ -717,35 +732,48 @@ class Core(component.Component):
|
||||
@export
|
||||
def queue_up(self, torrent_ids):
|
||||
log.debug("Attempting to queue %s to up", torrent_ids)
|
||||
torrents = ((self.torrentmanager.get_queue_position(torrent_id), torrent_id) for torrent_id in torrent_ids)
|
||||
torrent_moved = True
|
||||
prev_queue_position = None
|
||||
#torrent_ids must be sorted before moving.
|
||||
torrent_ids = list(torrent_ids)
|
||||
torrent_ids.sort(key = lambda id: self.torrentmanager.torrents[id].get_queue_position())
|
||||
for torrent_id in torrent_ids:
|
||||
try:
|
||||
# If the queue method returns True, then we should emit a signal
|
||||
if self.torrentmanager.queue_up(torrent_id):
|
||||
component.get("EventManager").emit(TorrentQueueChangedEvent())
|
||||
except KeyError:
|
||||
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
||||
for queue_position, torrent_id in sorted(torrents):
|
||||
# Move the torrent if and only if there is space (by not moving it we preserve the order)
|
||||
if torrent_moved or queue_position - prev_queue_position > 1:
|
||||
try:
|
||||
torrent_moved = self.torrentmanager.queue_up(torrent_id)
|
||||
except KeyError:
|
||||
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
||||
# If the torrent moved, then we should emit a signal
|
||||
if torrent_moved:
|
||||
component.get("EventManager").emit(TorrentQueueChangedEvent())
|
||||
else:
|
||||
prev_queue_position = queue_position
|
||||
|
||||
@export
|
||||
def queue_down(self, torrent_ids):
|
||||
log.debug("Attempting to queue %s to down", torrent_ids)
|
||||
torrents = ((self.torrentmanager.get_queue_position(torrent_id), torrent_id) for torrent_id in torrent_ids)
|
||||
torrent_moved = True
|
||||
prev_queue_position = None
|
||||
#torrent_ids must be sorted before moving.
|
||||
torrent_ids = list(torrent_ids)
|
||||
torrent_ids.sort(key = lambda id: -self.torrentmanager.torrents[id].get_queue_position())
|
||||
for torrent_id in torrent_ids:
|
||||
try:
|
||||
# If the queue method returns True, then we should emit a signal
|
||||
if self.torrentmanager.queue_down(torrent_id):
|
||||
component.get("EventManager").emit(TorrentQueueChangedEvent())
|
||||
except KeyError:
|
||||
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
||||
for queue_position, torrent_id in sorted(torrents, reverse=True):
|
||||
# Move the torrent if and only if there is space (by not moving it we preserve the order)
|
||||
if torrent_moved or prev_queue_position - queue_position > 1:
|
||||
try:
|
||||
torrent_moved = self.torrentmanager.queue_down(torrent_id)
|
||||
except KeyError:
|
||||
log.warning("torrent_id: %s does not exist in the queue", torrent_id)
|
||||
# If the torrent moved, then we should emit a signal
|
||||
if torrent_moved:
|
||||
component.get("EventManager").emit(TorrentQueueChangedEvent())
|
||||
else:
|
||||
prev_queue_position = queue_position
|
||||
|
||||
@export
|
||||
def queue_bottom(self, torrent_ids):
|
||||
log.debug("Attempting to queue %s to bottom", torrent_ids)
|
||||
for torrent_id in torrent_ids:
|
||||
# torrent_ids must be sorted before moving to preserve order
|
||||
for torrent_id in sorted(torrent_ids, key=self.torrentmanager.get_queue_position):
|
||||
try:
|
||||
# If the queue method returns True, then we should emit a signal
|
||||
if self.torrentmanager.queue_bottom(torrent_id):
|
||||
|
@ -62,13 +62,8 @@ class Daemon(object):
|
||||
|
||||
def process_running(pid):
|
||||
if deluge.common.windows_check():
|
||||
# Do some fancy WMI junk to see if the PID exists in Windows
|
||||
from win32com.client import GetObject
|
||||
def get_proclist():
|
||||
WMI = GetObject('winmgmts:')
|
||||
processes = WMI.InstancesOf('Win32_Process')
|
||||
return [process.Properties_('ProcessID').Value for process in processes]
|
||||
return pid in get_proclist()
|
||||
import win32process
|
||||
return pid in win32process.EnumProcesses()
|
||||
else:
|
||||
# We can just use os.kill on UNIX to test if the process is running
|
||||
try:
|
||||
|
@ -55,8 +55,8 @@ class EventManager(component.Component):
|
||||
#log.debug("Running handler %s for event %s with args: %s", event.name, handler, event.args)
|
||||
try:
|
||||
handler(*event.args)
|
||||
except:
|
||||
log.error("Event handler %s failed in %s", event.name, handler)
|
||||
except Exception, e:
|
||||
log.error("Event handler %s failed in %s with exception %s", event.name, handler, e)
|
||||
|
||||
def register_event_handler(self, event, handler):
|
||||
"""
|
||||
|
@ -126,12 +126,11 @@ class FilterManager(component.Component):
|
||||
|
||||
#sanitize input: filter-value must be a list of strings
|
||||
for key, value in filter_dict.items():
|
||||
if isinstance(value, str):
|
||||
filter_dict[key] = [value]
|
||||
if isinstance(value, basestring):
|
||||
filter_dict[key] = [value]
|
||||
|
||||
|
||||
if "id"in filter_dict: #optimized filter for id:
|
||||
torrent_ids = filter_dict["id"]
|
||||
if "id" in filter_dict: #optimized filter for id:
|
||||
torrent_ids = list(filter_dict["id"])
|
||||
del filter_dict["id"]
|
||||
else:
|
||||
torrent_ids = self.torrents.get_torrent_list()
|
||||
|
@ -251,7 +251,7 @@ class PreferencesManager(component.Component):
|
||||
# Only set the listen ports if random_port is not true
|
||||
if self.config["random_port"] is not True:
|
||||
log.debug("listen port range set to %s-%s", value[0], value[1])
|
||||
self.session.listen_on(value[0], value[1], str(self.config["listen_interface"]))
|
||||
self.session.listen_on(value[0], value[1], str(self.config["listen_interface"]).strip())
|
||||
|
||||
def _on_set_listen_interface(self, key, value):
|
||||
# Call the random_port callback since it'll do what we need
|
||||
@ -273,7 +273,7 @@ class PreferencesManager(component.Component):
|
||||
# Set the listen ports
|
||||
log.debug("listen port range set to %s-%s", listen_ports[0],
|
||||
listen_ports[1])
|
||||
self.session.listen_on(listen_ports[0], listen_ports[1], str(self.config["listen_interface"]))
|
||||
self.session.listen_on(listen_ports[0], listen_ports[1], str(self.config["listen_interface"]).strip())
|
||||
|
||||
def _on_set_outgoing_ports(self, key, value):
|
||||
if not self.config["random_outgoing_ports"]:
|
||||
@ -338,15 +338,19 @@ class PreferencesManager(component.Component):
|
||||
def _on_set_utpex(self, key, value):
|
||||
log.debug("utpex value set to %s", value)
|
||||
if value:
|
||||
self.session.add_extension(lt.create_ut_pex_plugin)
|
||||
# Note: All libtorrent python bindings to set plugins/extensions need to be disabled
|
||||
# due to GIL issue. https://code.google.com/p/libtorrent/issues/detail?id=369
|
||||
#self.session.add_extension(lt.create_ut_pex_plugin)
|
||||
pass
|
||||
|
||||
def _on_set_encryption(self, key, value):
|
||||
log.debug("encryption value %s set to %s..", key, value)
|
||||
pe_enc_level = {0: lt.enc_level.plaintext, 1: lt.enc_level.rc4, 2: lt.enc_level.both}
|
||||
pe_settings = lt.pe_settings()
|
||||
pe_settings.out_enc_policy = \
|
||||
lt.enc_policy(self.config["enc_out_policy"])
|
||||
pe_settings.in_enc_policy = lt.enc_policy(self.config["enc_in_policy"])
|
||||
pe_settings.allowed_enc_level = lt.enc_level(self.config["enc_level"])
|
||||
pe_settings.allowed_enc_level = lt.enc_level(pe_enc_level[self.config["enc_level"]])
|
||||
pe_settings.prefer_rc4 = self.config["enc_prefer_rc4"]
|
||||
self.session.set_pe_settings(pe_settings)
|
||||
set = self.session.get_pe_settings()
|
||||
@ -468,15 +472,14 @@ class PreferencesManager(component.Component):
|
||||
|
||||
def _on_set_proxies(self, key, value):
|
||||
for k, v in value.items():
|
||||
if v["type"]:
|
||||
proxy_settings = lt.proxy_settings()
|
||||
proxy_settings.type = lt.proxy_type(v["type"])
|
||||
proxy_settings.username = str(v["username"])
|
||||
proxy_settings.password = str(v["password"])
|
||||
proxy_settings.hostname = str(v["hostname"])
|
||||
proxy_settings.port = v["port"]
|
||||
log.debug("setting %s proxy settings", k)
|
||||
getattr(self.session, "set_%s_proxy" % k)(proxy_settings)
|
||||
proxy_settings = lt.proxy_settings()
|
||||
proxy_settings.type = lt.proxy_type(v["type"])
|
||||
proxy_settings.username = str(v["username"])
|
||||
proxy_settings.password = str(v["password"])
|
||||
proxy_settings.hostname = str(v["hostname"])
|
||||
proxy_settings.port = v["port"]
|
||||
log.debug("setting %s proxy settings", k)
|
||||
getattr(self.session, "set_%s_proxy" % k)(proxy_settings)
|
||||
|
||||
def _on_rate_limit_ip_overhead(self, key, value):
|
||||
log.debug("%s: %s", key, value)
|
||||
|
@ -465,7 +465,7 @@ class RPCServer(component.Component):
|
||||
"""
|
||||
log.debug("intevents: %s", self.factory.interested_events)
|
||||
# Find sessions interested in this event
|
||||
for session_id, interest in self.factory.interested_events.iteritems():
|
||||
for session_id, interest in self.factory.interested_events.items():
|
||||
if event.name in interest:
|
||||
log.debug("Emit Event: %s %s", event.name, event.args)
|
||||
# This session is interested so send a RPC_EVENT
|
||||
|
@ -174,7 +174,7 @@ class Torrent(object):
|
||||
self.trackers = []
|
||||
# Create a list of trackers
|
||||
for value in self.handle.trackers():
|
||||
if lt.version_minor < 15:
|
||||
if lt.version_major == 0 and lt.version_minor < 15:
|
||||
tracker = {}
|
||||
tracker["url"] = value.url
|
||||
tracker["tier"] = value.tier
|
||||
@ -260,11 +260,11 @@ class Torrent(object):
|
||||
self.handle.set_download_limit(v)
|
||||
|
||||
def set_prioritize_first_last(self, prioritize):
|
||||
self.options["prioritize_first_last_pieces"] = prioritize
|
||||
if prioritize:
|
||||
if self.handle.has_metadata():
|
||||
if self.handle.get_torrent_info().num_files() == 1:
|
||||
# We only do this if one file is in the torrent
|
||||
self.options["prioritize_first_last_pieces"] = prioritize
|
||||
priorities = [1] * self.handle.get_torrent_info().num_pieces()
|
||||
priorities[0] = 7
|
||||
priorities[-1] = 7
|
||||
@ -478,7 +478,8 @@ class Torrent(object):
|
||||
for index, file in enumerate(files):
|
||||
ret.append({
|
||||
'index': index,
|
||||
'path': file.path.decode("utf8", "ignore"),
|
||||
# Make path separators consistent across platforms
|
||||
'path': file.path.decode("utf8").replace('\\', '/'),
|
||||
'size': file.size,
|
||||
'offset': file.offset
|
||||
})
|
||||
@ -667,7 +668,7 @@ class Torrent(object):
|
||||
|
||||
def ti_name():
|
||||
if self.handle.has_metadata():
|
||||
name = self.torrent_info.file_at(0).path.split("/", 1)[0]
|
||||
name = self.torrent_info.file_at(0).path.replace("\\", "/", 1).split("/", 1)[0]
|
||||
if not name:
|
||||
name = self.torrent_info.name()
|
||||
try:
|
||||
@ -833,26 +834,30 @@ 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:
|
||||
dest_u = unicode(dest, "utf-8")
|
||||
dest = unicode(dest, "utf-8")
|
||||
except TypeError:
|
||||
# String is already unicode
|
||||
dest_u = dest
|
||||
pass
|
||||
|
||||
if not os.path.exists(dest_u):
|
||||
if not os.path.exists(dest):
|
||||
try:
|
||||
# Try to make the destination path if it doesn't exist
|
||||
os.makedirs(dest_u)
|
||||
os.makedirs(dest)
|
||||
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
|
||||
|
||||
dest_bytes = dest.encode('utf-8')
|
||||
try:
|
||||
self.handle.move_storage(dest_u)
|
||||
except:
|
||||
# libtorrent needs unicode object if wstrings are enabled, utf8 bytestring otherwise
|
||||
try:
|
||||
self.handle.move_storage(dest)
|
||||
except TypeError:
|
||||
self.handle.move_storage(dest_bytes)
|
||||
except Exception, e:
|
||||
log.error("Error calling libtorrent move_storage: %s" % e)
|
||||
return False
|
||||
|
||||
return True
|
||||
@ -863,6 +868,11 @@ class Torrent(object):
|
||||
self.handle.save_resume_data()
|
||||
self.waiting_on_resume_data = True
|
||||
|
||||
def on_metadata_received(self):
|
||||
if self.options["prioritize_first_last_pieces"]:
|
||||
self.set_prioritize_first_last(True)
|
||||
self.write_torrentfile()
|
||||
|
||||
def write_torrentfile(self):
|
||||
"""Writes the torrent file"""
|
||||
path = "%s/%s.torrent" % (
|
||||
@ -928,8 +938,17 @@ class Torrent(object):
|
||||
"""Renames files in the torrent. 'filenames' should be a list of
|
||||
(index, filename) pairs."""
|
||||
for index, filename in filenames:
|
||||
# Make sure filename is a unicode object
|
||||
try:
|
||||
filename = unicode(filename, "utf-8")
|
||||
except TypeError:
|
||||
pass
|
||||
filename = sanitize_filepath(filename)
|
||||
self.handle.rename_file(index, filename.encode("utf-8"))
|
||||
# libtorrent needs unicode object if wstrings are enabled, utf8 bytestring otherwise
|
||||
try:
|
||||
self.handle.rename_file(index, filename)
|
||||
except TypeError:
|
||||
self.handle.rename_file(index, filename.encode("utf-8"))
|
||||
|
||||
def rename_folder(self, folder, new_folder):
|
||||
"""Renames a folder within a torrent. This basically does a file rename
|
||||
@ -946,7 +965,11 @@ class Torrent(object):
|
||||
if f["path"].startswith(folder):
|
||||
# Keep a list of filerenames we're waiting on
|
||||
wait_on_folder[2].append(f["index"])
|
||||
self.handle.rename_file(f["index"], f["path"].replace(folder, new_folder, 1).encode("utf-8"))
|
||||
new_path = f["path"].replace(folder, new_folder, 1)
|
||||
try:
|
||||
self.handle.rename_file(f["index"], new_path)
|
||||
except TypeError:
|
||||
self.handle.rename_file(f["index"], new_path.encode("utf-8"))
|
||||
self.waiting_on_folder_rename.append(wait_on_folder)
|
||||
|
||||
def cleanup_prev_status(self):
|
||||
|
@ -55,7 +55,7 @@ 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.common import utf8_encoded
|
||||
from deluge.common import utf8_encoded, decode_string
|
||||
|
||||
from deluge.log import LOG as log
|
||||
|
||||
@ -138,6 +138,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
# Create the torrents dict { torrent_id: Torrent }
|
||||
self.torrents = {}
|
||||
self.queued_torrents = set()
|
||||
|
||||
# This is a list of torrent_id when we shutdown the torrentmanager.
|
||||
# We use this list to determine if all active torrents have been paused
|
||||
@ -147,6 +148,9 @@ class TorrentManager(component.Component):
|
||||
# self.num_resume_data used to save resume_data in bulk
|
||||
self.num_resume_data = 0
|
||||
|
||||
# Keep track of torrents finished but moving storage
|
||||
self.waiting_on_finish_moving = []
|
||||
|
||||
# Keeps track of resume data that needs to be saved to disk
|
||||
self.resume_data = {}
|
||||
|
||||
@ -180,6 +184,8 @@ class TorrentManager(component.Component):
|
||||
self.on_alert_tracker_error)
|
||||
self.alerts.register_handler("storage_moved_alert",
|
||||
self.on_alert_storage_moved)
|
||||
self.alerts.register_handler("storage_moved_failed_alert",
|
||||
self.on_alert_storage_moved_failed)
|
||||
self.alerts.register_handler("torrent_resumed_alert",
|
||||
self.on_alert_torrent_resumed)
|
||||
self.alerts.register_handler("state_changed_alert",
|
||||
@ -375,14 +381,20 @@ class TorrentManager(component.Component):
|
||||
resume_data = self.legacy_get_resume_data_from_file(state.torrent_id)
|
||||
self.legacy_delete_resume_data(state.torrent_id)
|
||||
|
||||
add_torrent_params["resume_data"] = resume_data
|
||||
if resume_data:
|
||||
add_torrent_params["resume_data"] = resume_data
|
||||
else:
|
||||
# We have a torrent_info object or magnet uri so we're not loading from state.
|
||||
if torrent_info:
|
||||
add_torrent_id = str(torrent_info.info_hash())
|
||||
# If this torrent id is already in the session, merge any additional trackers.
|
||||
if add_torrent_id in self.get_torrent_list():
|
||||
# Torrent already exists just append any extra trackers.
|
||||
log.debug("Torrent (%s) exists, checking for trackers to add...", add_torrent_id)
|
||||
log.info("Merging trackers for torrent (%s) already in session...", add_torrent_id)
|
||||
# Don't merge trackers if either torrent has private flag set
|
||||
if self[add_torrent_id].get_status(["private"])["private"]:
|
||||
log.info("Merging trackers abandoned: Torrent has private flag set.")
|
||||
return
|
||||
|
||||
add_torrent_trackers = []
|
||||
for value in torrent_info.trackers():
|
||||
tracker = {}
|
||||
@ -392,17 +404,18 @@ class TorrentManager(component.Component):
|
||||
|
||||
torrent_trackers = {}
|
||||
tracker_list = []
|
||||
for tracker in self[add_torrent_id].get_status(["trackers"])["trackers"]:
|
||||
for tracker in self[add_torrent_id].get_status(["trackers"])["trackers"]:
|
||||
torrent_trackers[(tracker["url"])] = tracker
|
||||
tracker_list.append(tracker)
|
||||
|
||||
added_tracker = False
|
||||
added_tracker = 0
|
||||
for tracker in add_torrent_trackers:
|
||||
if tracker['url'] not in torrent_trackers:
|
||||
tracker_list.append(tracker)
|
||||
added_tracker = True
|
||||
added_tracker += 1
|
||||
|
||||
if added_tracker:
|
||||
log.info("%s tracker(s) merged into torrent.", added_tracker)
|
||||
self[add_torrent_id].set_trackers(tracker_list)
|
||||
return
|
||||
|
||||
@ -417,13 +430,19 @@ class TorrentManager(component.Component):
|
||||
# Check for renamed files and if so, rename them in the torrent_info
|
||||
# before adding to the session.
|
||||
if options["mapped_files"]:
|
||||
for index, filename in options["mapped_files"].items():
|
||||
filename = deluge.core.torrent.sanitize_filepath(filename)
|
||||
log.debug("renaming file index %s to %s", index, filename)
|
||||
torrent_info.rename_file(index, utf8_encoded(filename))
|
||||
for index, fname in options["mapped_files"].items():
|
||||
try:
|
||||
fname = unicode(fname, "utf-8")
|
||||
except TypeError:
|
||||
pass
|
||||
fname = deluge.core.torrent.sanitize_filepath(fname)
|
||||
log.debug("renaming file index %s to %s", index, fname)
|
||||
try:
|
||||
torrent_info.rename_file(index, fname)
|
||||
except TypeError:
|
||||
torrent_info.rename_file(index, fname.encode("utf-8"))
|
||||
|
||||
add_torrent_params["ti"] = torrent_info
|
||||
add_torrent_params["resume_data"] = ""
|
||||
|
||||
#log.info("Adding torrent: %s", filename)
|
||||
log.debug("options: %s", options)
|
||||
@ -476,6 +495,9 @@ class TorrentManager(component.Component):
|
||||
if not options["add_paused"]:
|
||||
torrent.resume()
|
||||
|
||||
# Add to queued torrents set
|
||||
self.queued_torrents.add(torrent.torrent_id)
|
||||
|
||||
# Write the .torrent file to the state directory
|
||||
if filedump:
|
||||
try:
|
||||
@ -542,12 +564,11 @@ class TorrentManager(component.Component):
|
||||
:raises InvalidTorrentError: if the torrent_id is not in the session
|
||||
|
||||
"""
|
||||
|
||||
if torrent_id not in self.torrents:
|
||||
try:
|
||||
torrent_name = self.torrents[torrent_id].get_status(["name"])["name"]
|
||||
except KeyError:
|
||||
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))
|
||||
|
||||
@ -584,10 +605,17 @@ class TorrentManager(component.Component):
|
||||
# Stop the looping call
|
||||
self.torrents[torrent_id].prev_status_cleanup_loop.stop()
|
||||
|
||||
# Remove from set if it wasn't finished
|
||||
if not self.torrents[torrent_id].is_finished:
|
||||
try:
|
||||
self.queued_torrents.remove(torrent_id)
|
||||
except KeyError:
|
||||
log.debug("%s isn't in queued torrents set?", torrent_id)
|
||||
|
||||
# Remove the torrent from deluge's session
|
||||
try:
|
||||
del self.torrents[torrent_id]
|
||||
except KeyError, ValueError:
|
||||
except (KeyError, ValueError):
|
||||
return False
|
||||
|
||||
# Save the session state
|
||||
@ -623,7 +651,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
# Reorder the state.torrents list to add torrents in the correct queue
|
||||
# order.
|
||||
state.torrents.sort(key=operator.attrgetter("queue"))
|
||||
state.torrents.sort(key=operator.attrgetter("queue"), reverse=self.config["queue_new_to_top"])
|
||||
|
||||
resume_data = self.load_resume_data_file()
|
||||
|
||||
@ -747,6 +775,7 @@ class TorrentManager(component.Component):
|
||||
return
|
||||
|
||||
path = os.path.join(get_config_dir(), "state", "torrents.fastresume")
|
||||
path_tmp = path + ".tmp"
|
||||
|
||||
# First step is to load the existing file and update the dictionary
|
||||
if resume_data is None:
|
||||
@ -757,11 +786,12 @@ class TorrentManager(component.Component):
|
||||
|
||||
try:
|
||||
log.debug("Saving fastresume file: %s", path)
|
||||
fastresume_file = open(path, "wb")
|
||||
fastresume_file = open(path_tmp, "wb")
|
||||
fastresume_file.write(lt.bencode(resume_data))
|
||||
fastresume_file.flush()
|
||||
os.fsync(fastresume_file.fileno())
|
||||
fastresume_file.close()
|
||||
shutil.move(path_tmp, path)
|
||||
except IOError:
|
||||
log.warning("Error trying to save fastresume file")
|
||||
|
||||
@ -771,10 +801,10 @@ class TorrentManager(component.Component):
|
||||
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'])
|
||||
try:
|
||||
info = self.torrents[torrent_id].get_status(['save_path'])
|
||||
except KeyError:
|
||||
raise InvalidTorrentError("torrent_id not in session")
|
||||
# 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)
|
||||
@ -798,6 +828,10 @@ class TorrentManager(component.Component):
|
||||
except OSError, (errno, strerror):
|
||||
log.debug("Cannot Remove Folder: %s (ErrNo %s)", strerror, errno)
|
||||
|
||||
def get_queue_position(self, torrent_id):
|
||||
"""Get queue position of torrent"""
|
||||
return self.torrents[torrent_id].get_queue_position()
|
||||
|
||||
def queue_top(self, torrent_id):
|
||||
"""Queue torrent to top"""
|
||||
if self.torrents[torrent_id].get_queue_position() == 0:
|
||||
@ -816,7 +850,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
def queue_down(self, torrent_id):
|
||||
"""Queue torrent down one position"""
|
||||
if self.torrents[torrent_id].get_queue_position() == (len(self.torrents) - 1):
|
||||
if self.torrents[torrent_id].get_queue_position() == (len(self.queued_torrents) - 1):
|
||||
return False
|
||||
|
||||
self.torrents[torrent_id].handle.queue_position_down()
|
||||
@ -824,7 +858,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
def queue_bottom(self, torrent_id):
|
||||
"""Queue torrent to bottom"""
|
||||
if self.torrents[torrent_id].get_queue_position() == (len(self.torrents) - 1):
|
||||
if self.torrents[torrent_id].get_queue_position() == (len(self.queued_torrents) - 1):
|
||||
return False
|
||||
|
||||
self.torrents[torrent_id].handle.queue_position_bottom()
|
||||
@ -866,19 +900,25 @@ class TorrentManager(component.Component):
|
||||
# that the torrent wasn't downloaded, but just added.
|
||||
total_download = torrent.get_status(["total_payload_download"])["total_payload_download"]
|
||||
|
||||
# Move completed download to completed folder if needed
|
||||
if not torrent.is_finished and total_download:
|
||||
move_path = None
|
||||
|
||||
if torrent.options["move_completed"]:
|
||||
move_path = torrent.options["move_completed_path"]
|
||||
if torrent.options["download_location"] != move_path:
|
||||
torrent.move_storage(move_path)
|
||||
|
||||
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
|
||||
|
||||
torrent.is_finished = True
|
||||
torrent.update_state()
|
||||
if not torrent.is_finished and total_download:
|
||||
# Move completed download to completed folder if needed
|
||||
if torrent.options["move_completed"] and \
|
||||
torrent.options["download_location"] != torrent.options["move_completed_path"]:
|
||||
self.waiting_on_finish_moving.append(torrent_id)
|
||||
torrent.move_storage(torrent.options["move_completed_path"])
|
||||
else:
|
||||
torrent.is_finished = True
|
||||
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
|
||||
else:
|
||||
torrent.is_finished = True
|
||||
|
||||
# Torrent is no longer part of the queue
|
||||
try:
|
||||
self.queued_torrents.remove(torrent_id)
|
||||
except KeyError:
|
||||
# Sometimes libtorrent fires a TorrentFinishedEvent twice
|
||||
log.debug("%s isn't in queued torrents set?", torrent_id)
|
||||
|
||||
# Only save resume data if it was actually downloaded something. Helps
|
||||
# on startup with big queues with lots of seeding torrents. Libtorrent
|
||||
@ -928,7 +968,7 @@ class TorrentManager(component.Component):
|
||||
torrent.update_state()
|
||||
|
||||
def on_alert_tracker_reply(self, alert):
|
||||
log.debug("on_alert_tracker_reply: %s", alert.message().decode("utf8"))
|
||||
log.debug("on_alert_tracker_reply: %s", decode_string(alert.message()))
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
@ -960,7 +1000,7 @@ class TorrentManager(component.Component):
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
tracker_status = '%s: %s' % (_("Warning"), str(alert.message()))
|
||||
tracker_status = '%s: %s' % (_("Warning"), decode_string(alert.message()))
|
||||
# Set the tracker status for the torrent
|
||||
torrent.set_tracker_status(tracker_status)
|
||||
|
||||
@ -976,12 +1016,32 @@ class TorrentManager(component.Component):
|
||||
def on_alert_storage_moved(self, alert):
|
||||
log.debug("on_alert_storage_moved")
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
torrent = self.torrents[torrent_id]
|
||||
except (RuntimeError, KeyError):
|
||||
return
|
||||
torrent.set_save_path(os.path.normpath(alert.handle.save_path()))
|
||||
torrent.set_move_completed(False)
|
||||
|
||||
if torrent_id in self.waiting_on_finish_moving:
|
||||
self.waiting_on_finish_moving.remove(torrent_id)
|
||||
torrent.is_finished = True
|
||||
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
|
||||
|
||||
def on_alert_storage_moved_failed(self, alert):
|
||||
"""Alert handler for libtorrent storage_moved_failed_alert"""
|
||||
log.debug("on_alert_storage_moved_failed: %s", decode_string(alert.message()))
|
||||
try:
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
torrent = self.torrents[torrent_id]
|
||||
except (RuntimeError, KeyError):
|
||||
return
|
||||
|
||||
if torrent_id in self.waiting_on_finish_moving:
|
||||
self.waiting_on_finish_moving.remove(torrent_id)
|
||||
torrent.is_finished = True
|
||||
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
|
||||
|
||||
def on_alert_torrent_resumed(self, alert):
|
||||
log.debug("on_alert_torrent_resumed")
|
||||
try:
|
||||
@ -1010,6 +1070,7 @@ class TorrentManager(component.Component):
|
||||
# Torrent may need to download data after checking.
|
||||
if torrent.state in ('Checking', 'Checking Resume Data', 'Downloading'):
|
||||
torrent.is_finished = False
|
||||
self.queued_torrents.add(torrent_id)
|
||||
|
||||
# Only emit a state changed event if the state has actually changed
|
||||
if torrent.state != old_state:
|
||||
@ -1032,7 +1093,7 @@ class TorrentManager(component.Component):
|
||||
self.save_resume_data_file()
|
||||
|
||||
def on_alert_save_resume_data_failed(self, alert):
|
||||
log.debug("on_alert_save_resume_data_failed: %s", alert.message())
|
||||
log.debug("on_alert_save_resume_data_failed: %s", decode_string(alert.message()))
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
@ -1046,7 +1107,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
def on_alert_file_renamed(self, alert):
|
||||
log.debug("on_alert_file_renamed")
|
||||
log.debug("index: %s name: %s", alert.index, alert.name.decode("utf8"))
|
||||
log.debug("index: %s name: %s", alert.index, decode_string(alert.name))
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
@ -1081,10 +1142,10 @@ class TorrentManager(component.Component):
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
torrent.write_torrentfile()
|
||||
torrent.on_metadata_received()
|
||||
|
||||
def on_alert_file_error(self, alert):
|
||||
log.debug("on_alert_file_error: %s", alert.message())
|
||||
log.debug("on_alert_file_error: %s", decode_string(alert.message()))
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
@ -1092,7 +1153,7 @@ class TorrentManager(component.Component):
|
||||
torrent.update_state()
|
||||
|
||||
def on_alert_file_completed(self, alert):
|
||||
log.debug("file_completed_alert: %s", alert.message())
|
||||
log.debug("file_completed_alert: %s", decode_string(alert.message()))
|
||||
try:
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
except:
|
||||
|
@ -9,6 +9,7 @@ Exec=deluge-gtk %U
|
||||
Icon=deluge
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;FileTransfer;P2P;GTK
|
||||
Categories=Network;FileTransfer;P2P;GTK;
|
||||
StartupWMClass=Deluge
|
||||
StartupNotify=true
|
||||
MimeType=application/x-bittorrent;x-scheme-handler/magnet;
|
||||
|
@ -192,7 +192,17 @@ def download_file(url, filename, callback=None, headers=None, force_filename=Fal
|
||||
headers = {}
|
||||
headers["accept-encoding"] = "deflate, gzip, x-gzip"
|
||||
|
||||
scheme, host, port, path = client._parse(url)
|
||||
# In twisted 13.1.0 the _parse() function was replaced by the _URI class
|
||||
if hasattr(client, '_parse'):
|
||||
scheme, host, port, path = client._parse(url)
|
||||
else:
|
||||
from twisted.web.client import _URI
|
||||
uri = _URI.fromBytes(url)
|
||||
scheme = uri.scheme
|
||||
host = uri.host
|
||||
port = uri.port
|
||||
path = uri.path
|
||||
|
||||
factory = HTTPDownloader(url, filename, callback, headers, force_filename, allow_compression)
|
||||
if scheme == "https":
|
||||
from twisted.internet import ssl
|
||||
|
3977
deluge/i18n/af.po
Normal file
3977
deluge/i18n/af.po
Normal file
File diff suppressed because it is too large
Load Diff
1851
deluge/i18n/ar.po
1851
deluge/i18n/ar.po
File diff suppressed because it is too large
Load Diff
@ -2224,7 +2224,7 @@ msgid "Up:"
|
||||
msgstr "Unvíu:"
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1827
deluge/i18n/be.po
1827
deluge/i18n/be.po
File diff suppressed because it is too large
Load Diff
1827
deluge/i18n/bg.po
1827
deluge/i18n/bg.po
File diff suppressed because it is too large
Load Diff
1813
deluge/i18n/bn.po
1813
deluge/i18n/bn.po
File diff suppressed because it is too large
Load Diff
1813
deluge/i18n/bs.po
1813
deluge/i18n/bs.po
File diff suppressed because it is too large
Load Diff
1991
deluge/i18n/ca.po
1991
deluge/i18n/ca.po
File diff suppressed because it is too large
Load Diff
1829
deluge/i18n/cs.po
1829
deluge/i18n/cs.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1846
deluge/i18n/da.po
1846
deluge/i18n/da.po
File diff suppressed because it is too large
Load Diff
2016
deluge/i18n/de.po
2016
deluge/i18n/de.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1969
deluge/i18n/el.po
1969
deluge/i18n/el.po
File diff suppressed because it is too large
Load Diff
2100
deluge/i18n/en_AU.po
2100
deluge/i18n/en_AU.po
File diff suppressed because it is too large
Load Diff
2078
deluge/i18n/en_CA.po
2078
deluge/i18n/en_CA.po
File diff suppressed because it is too large
Load Diff
1873
deluge/i18n/en_GB.po
1873
deluge/i18n/en_GB.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1845
deluge/i18n/es.po
1845
deluge/i18n/es.po
File diff suppressed because it is too large
Load Diff
1903
deluge/i18n/et.po
1903
deluge/i18n/et.po
File diff suppressed because it is too large
Load Diff
2331
deluge/i18n/eu.po
2331
deluge/i18n/eu.po
File diff suppressed because it is too large
Load Diff
1985
deluge/i18n/fa.po
1985
deluge/i18n/fa.po
File diff suppressed because it is too large
Load Diff
1962
deluge/i18n/fi.po
1962
deluge/i18n/fi.po
File diff suppressed because it is too large
Load Diff
1813
deluge/i18n/fo.po
1813
deluge/i18n/fo.po
File diff suppressed because it is too large
Load Diff
1866
deluge/i18n/fr.po
1866
deluge/i18n/fr.po
File diff suppressed because it is too large
Load Diff
@ -2223,7 +2223,7 @@ msgid "Up:"
|
||||
msgstr "Omheech:"
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
3962
deluge/i18n/ga.po
Normal file
3962
deluge/i18n/ga.po
Normal file
File diff suppressed because it is too large
Load Diff
2139
deluge/i18n/gl.po
2139
deluge/i18n/gl.po
File diff suppressed because it is too large
Load Diff
1827
deluge/i18n/he.po
1827
deluge/i18n/he.po
File diff suppressed because it is too large
Load Diff
1827
deluge/i18n/hi.po
1827
deluge/i18n/hi.po
File diff suppressed because it is too large
Load Diff
1914
deluge/i18n/hr.po
1914
deluge/i18n/hr.po
File diff suppressed because it is too large
Load Diff
1831
deluge/i18n/hu.po
1831
deluge/i18n/hu.po
File diff suppressed because it is too large
Load Diff
1813
deluge/i18n/id.po
1813
deluge/i18n/id.po
File diff suppressed because it is too large
Load Diff
1845
deluge/i18n/is.po
1845
deluge/i18n/is.po
File diff suppressed because it is too large
Load Diff
1840
deluge/i18n/it.po
1840
deluge/i18n/it.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1925
deluge/i18n/ja.po
1925
deluge/i18n/ja.po
File diff suppressed because it is too large
Load Diff
3225
deluge/i18n/ka.po
3225
deluge/i18n/ka.po
File diff suppressed because it is too large
Load Diff
1831
deluge/i18n/kk.po
1831
deluge/i18n/kk.po
File diff suppressed because it is too large
Load Diff
3970
deluge/i18n/km.po
Normal file
3970
deluge/i18n/km.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1827
deluge/i18n/ko.po
1827
deluge/i18n/ko.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
3962
deluge/i18n/ky.po
Normal file
3962
deluge/i18n/ky.po
Normal file
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1813
deluge/i18n/lb.po
1813
deluge/i18n/lb.po
File diff suppressed because it is too large
Load Diff
2250
deluge/i18n/lt.po
2250
deluge/i18n/lt.po
File diff suppressed because it is too large
Load Diff
2227
deluge/i18n/lv.po
2227
deluge/i18n/lv.po
File diff suppressed because it is too large
Load Diff
2305
deluge/i18n/mk.po
2305
deluge/i18n/mk.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1876
deluge/i18n/ms.po
1876
deluge/i18n/ms.po
File diff suppressed because it is too large
Load Diff
3970
deluge/i18n/nap.po
Normal file
3970
deluge/i18n/nap.po
Normal file
File diff suppressed because it is too large
Load Diff
1836
deluge/i18n/nb.po
1836
deluge/i18n/nb.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1843
deluge/i18n/nl.po
1843
deluge/i18n/nl.po
File diff suppressed because it is too large
Load Diff
2774
deluge/i18n/nn.po
2774
deluge/i18n/nn.po
File diff suppressed because it is too large
Load Diff
1833
deluge/i18n/oc.po
1833
deluge/i18n/oc.po
File diff suppressed because it is too large
Load Diff
1906
deluge/i18n/pl.po
1906
deluge/i18n/pl.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
2838
deluge/i18n/pt.po
2838
deluge/i18n/pt.po
File diff suppressed because it is too large
Load Diff
2363
deluge/i18n/pt_BR.po
2363
deluge/i18n/pt_BR.po
File diff suppressed because it is too large
Load Diff
1946
deluge/i18n/ro.po
1946
deluge/i18n/ro.po
File diff suppressed because it is too large
Load Diff
1933
deluge/i18n/ru.po
1933
deluge/i18n/ru.po
File diff suppressed because it is too large
Load Diff
3578
deluge/i18n/si.po
3578
deluge/i18n/si.po
File diff suppressed because it is too large
Load Diff
1837
deluge/i18n/sk.po
1837
deluge/i18n/sk.po
File diff suppressed because it is too large
Load Diff
1832
deluge/i18n/sl.po
1832
deluge/i18n/sl.po
File diff suppressed because it is too large
Load Diff
1845
deluge/i18n/sr.po
1845
deluge/i18n/sr.po
File diff suppressed because it is too large
Load Diff
1837
deluge/i18n/sv.po
1837
deluge/i18n/sv.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1819
deluge/i18n/te.po
1819
deluge/i18n/te.po
File diff suppressed because it is too large
Load Diff
1813
deluge/i18n/th.po
1813
deluge/i18n/th.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1889
deluge/i18n/tr.po
1889
deluge/i18n/tr.po
File diff suppressed because it is too large
Load Diff
1955
deluge/i18n/uk.po
1955
deluge/i18n/uk.po
File diff suppressed because it is too large
Load Diff
1813
deluge/i18n/ur.po
1813
deluge/i18n/ur.po
File diff suppressed because it is too large
Load Diff
1813
deluge/i18n/vi.po
1813
deluge/i18n/vi.po
File diff suppressed because it is too large
Load Diff
1827
deluge/i18n/zh_CN.po
1827
deluge/i18n/zh_CN.po
File diff suppressed because it is too large
Load Diff
@ -2221,7 +2221,7 @@ msgid "Up:"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:59
|
||||
msgid "Copyright 2007-2011 Deluge Team"
|
||||
msgid "Copyright 2007-2014 Deluge Team"
|
||||
msgstr ""
|
||||
|
||||
#: deluge/ui/gtkui/aboutdialog.py:61
|
||||
|
1845
deluge/i18n/zh_TW.po
1845
deluge/i18n/zh_TW.po
File diff suppressed because it is too large
Load Diff
@ -42,6 +42,8 @@
|
||||
import os
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
from logging import FileHandler
|
||||
from errno import EEXIST
|
||||
|
||||
import deluge.log
|
||||
import deluge.common
|
||||
@ -86,6 +88,14 @@ def start_ui():
|
||||
# Get the options and args from the OptionParser
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
# Setup the logger
|
||||
if options.quiet:
|
||||
options.loglevel = "none"
|
||||
if options.loglevel:
|
||||
options.loglevel = options.loglevel.lower()
|
||||
deluge.log.setupLogger(level=options.loglevel, filename=options.logfile)
|
||||
from deluge.log import LOG as log
|
||||
|
||||
if options.config:
|
||||
if not os.path.exists(options.config):
|
||||
# Try to create the config folder if it doesn't exist
|
||||
@ -94,7 +104,7 @@ def start_ui():
|
||||
except Exception, e:
|
||||
pass
|
||||
elif not os.path.isdir(options.config):
|
||||
print "Config option needs to be a directory!"
|
||||
log.error("Config option needs to be a directory!")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if not os.path.exists(deluge.common.get_default_config_dir()):
|
||||
@ -110,18 +120,8 @@ def start_ui():
|
||||
print "The default UI has been changed to", options.default_ui
|
||||
sys.exit(0)
|
||||
|
||||
if options.quiet:
|
||||
options.loglevel = "none"
|
||||
|
||||
if options.loglevel:
|
||||
options.loglevel = options.loglevel.lower()
|
||||
|
||||
# Setup the logger
|
||||
deluge.log.setupLogger(level=options.loglevel, filename=options.logfile)
|
||||
|
||||
version = deluge.common.get_version()
|
||||
|
||||
from deluge.log import LOG as log
|
||||
log.info("Deluge ui %s", version)
|
||||
log.debug("options: %s", options)
|
||||
log.debug("args: %s", args)
|
||||
@ -170,18 +170,31 @@ this should be an IP address", metavar="IFACE",
|
||||
# Get the options and args from the OptionParser
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
# Setup the logger
|
||||
if options.quiet:
|
||||
options.loglevel = "none"
|
||||
if options.logfile:
|
||||
# Try to create the logfile's directory if it doesn't exist
|
||||
try:
|
||||
os.makedirs(os.path.abspath(os.path.dirname(options.logfile)))
|
||||
except OSError, e:
|
||||
if e.errno != EEXIST:
|
||||
print "There was an error creating the log directory, exiting... (%s)" % e
|
||||
sys.exit(1)
|
||||
deluge.log.setupLogger(level=options.loglevel, filename=options.logfile)
|
||||
from deluge.log import LOG as log
|
||||
|
||||
if options.config:
|
||||
if not deluge.configmanager.set_config_dir(options.config):
|
||||
print("There was an error setting the config dir! Exiting..")
|
||||
log.error("There was an error setting the config directory! Exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
# Sets the options.logfile to point to the default location
|
||||
def open_logfile():
|
||||
if not options.logfile:
|
||||
options.logfile = deluge.configmanager.get_config_dir("deluged.log")
|
||||
file_handler = FileHandler(options.logfile)
|
||||
log.addHandler(file_handler)
|
||||
|
||||
# Writes out a pidfile if necessary
|
||||
def write_pidfile():
|
||||
@ -208,15 +221,6 @@ this should be an IP address", metavar="IFACE",
|
||||
# Do not daemonize
|
||||
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
|
||||
|
||||
if options.profile:
|
||||
import hotshot
|
||||
hsp = hotshot.Profile(deluge.configmanager.get_config_dir("deluged.profile"))
|
||||
|
@ -174,6 +174,12 @@ class PluginManagerBase:
|
||||
info = {}.fromkeys(METADATA_KEYS)
|
||||
last_header = ""
|
||||
cont_lines = []
|
||||
# Missing plugin info
|
||||
if not self.pkg_env[name]:
|
||||
log.warn("Failed to retrive info for plugin '%s'" % name)
|
||||
for k in info:
|
||||
info[k] = _("Not available")
|
||||
return info
|
||||
for line in self.pkg_env[name][0].get_metadata("PKG-INFO").splitlines():
|
||||
if not line:
|
||||
continue
|
||||
@ -187,5 +193,4 @@ class PluginManagerBase:
|
||||
if line.split(":", 1)[0] in info.keys():
|
||||
last_header = line.split(":", 1)[0]
|
||||
info[last_header] = line.split(":", 1)[1].strip()
|
||||
|
||||
return info
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user