Compare commits
448 Commits
deluge-2.0
...
deluge-1.3
Author | SHA1 | Date | |
---|---|---|---|
bb89a355e5 | |||
954c0e2d4d | |||
9ce738e39a | |||
911d583ff0 | |||
07bc1322dd | |||
1058435700 | |||
0676aaf918 | |||
5d0dace131 | |||
be75d5021b | |||
b0029517eb | |||
2ae936df44 | |||
1f5cfd16e0 | |||
b5b0db6c60 | |||
1f660653ff | |||
7020e5ca90 | |||
00ebaae67a | |||
eb773bba2c | |||
e10cfb8f8c | |||
fcf26bad45 | |||
ec373c0ec9 | |||
933f80c01c | |||
89634137b9 | |||
e7dada6afc | |||
8db789ffe2 | |||
dd3aab1cef | |||
54769fe190 | |||
fa7edd0bad | |||
2b865273f6 | |||
403ad26111 | |||
6bc3968ba4 | |||
bbeb11b1e7 | |||
e4840d6b37 | |||
88db73e244 | |||
a359374547 | |||
c5e328c3bd | |||
bc425b392a | |||
4feb816380 | |||
ff95d9720a | |||
7847362dbb | |||
7b1e8862b4 | |||
ed883125fd | |||
9799c64505 | |||
d1f788ebe3 | |||
3744bdad69 | |||
ea75828f25 | |||
966fc6f64f | |||
5ff0d61b52 | |||
f5956f01e7 | |||
717db367e8 | |||
e0efe3885a | |||
836da50f78 | |||
85b1753a28 | |||
eb6959fb98 | |||
30c142ac4d | |||
08a75bd9f9 | |||
754a5a7f8a | |||
863fd7d2b7 | |||
56f2283e3e | |||
272d2005e0 | |||
375ee2dd1c | |||
98dcc8e3b1 | |||
e91458662f | |||
2600785cbb | |||
e5576dff1e | |||
4c648bc09f | |||
17d9739abb | |||
c1bf52e8d9 | |||
d60b436fcb | |||
2e0e0fb6b5 | |||
fe4f732c12 | |||
25d930b307 | |||
083c7fbb32 | |||
81fc47d080 | |||
e1745443bf | |||
649f933316 | |||
8b46ed8bdb | |||
62aa339fd8 | |||
96d8f10080 | |||
3d8e3f4add | |||
08eb6002f8 | |||
0015c9af86 | |||
4365e1ff39 | |||
0b6af77d57 | |||
6b2320d4b6 | |||
a5742f892d | |||
01adb882ea | |||
9fd6d3d418 | |||
d4834bc6c7 | |||
e4eda24e8f | |||
39f648684e | |||
56f8e213e3 | |||
cb86828fa6 | |||
5cffc8f7e0 | |||
97912a28e4 | |||
9d2b0101d6 | |||
e49b7d5c23 | |||
78a0ffd437 | |||
dee8c30968 | |||
a34f224201 | |||
7a3c4440dc | |||
ce0c6d99f2 | |||
d96fae520d | |||
d3c3d64cd4 | |||
b530658e20 | |||
eb70a7a6dc | |||
370035ffc5 | |||
0941377fac | |||
1a8aa4b920 | |||
1c8327034d | |||
9ed155c456 | |||
3d813ea1f8 | |||
0f962aeda5 | |||
3a0b6f8a6d | |||
cade8ee784 | |||
0d27032c06 | |||
3b8bbb2e77 | |||
9c9064b246 | |||
d1a3cbebbe | |||
79f8af688a | |||
bbba60f34c | |||
a2d313383c | |||
e336cd64b4 | |||
eff3577505 | |||
2504b2520a | |||
d8560f5c25 | |||
51802f7c54 | |||
a3538c8937 | |||
714b7f3c55 | |||
f0c4a4c766 | |||
683e4be529 | |||
93a860f2a1 | |||
9c4cd86492 | |||
fc6f9ebc3b | |||
603ca1b855 | |||
3b89595d38 | |||
448478485a | |||
4234583fc7 | |||
bdd9bd11b5 | |||
8dbdb02967 | |||
0555fbeb9d | |||
3126407d74 | |||
2d09035dc4 | |||
ed229d2ace | |||
9e0d173115 | |||
81d4b00ade | |||
1a9be0e9a4 | |||
28fc325db9 | |||
eb309813ea | |||
2e7bd90bda | |||
f59eca4405 | |||
fa209dfd5f | |||
8e8717c867 | |||
8644bc219a | |||
7c276f3133 | |||
ce1aca54b5 | |||
54642720e4 | |||
18ebf5b912 | |||
ff087d133c | |||
5d4c8241ea | |||
fbc664fa14 | |||
cc130c0085 | |||
87802aa965 | |||
678be3ce15 | |||
52f89270e6 | |||
1a9ae62669 | |||
e7b5be6a60 | |||
7e51c82705 | |||
d6e619c413 | |||
ba1cc6ef1f | |||
b5a0f32826 | |||
2cceb3a349 | |||
6ad3a770af | |||
f7c21fd87b | |||
01465f583f | |||
d3b0df5788 | |||
4f3c753fc1 | |||
6a873c524e | |||
010b6dd4af | |||
00900fef1c | |||
d2e1d66f43 | |||
c95ca18b37 | |||
34f81634e5 | |||
2cb77d17ce | |||
4c32aa14d0 | |||
5d9120b667 | |||
8a3bad9fc2 | |||
2005691312 | |||
47c9cccd74 | |||
2186fdb870 | |||
5b1e43735b | |||
e54f6c84d6 | |||
30d91f17dc | |||
af058bbdc7 | |||
7232dc4b01 | |||
263b10ffd2 | |||
36d5ff5040 | |||
85d4602949 | |||
a75fa41c42 | |||
e75ae7c81e | |||
fad24e93f3 | |||
94d53ac368 | |||
52bf08dfd1 | |||
532973c3d1 | |||
254efa88e5 | |||
583248f558 | |||
69956ad1db | |||
1e274cfb48 | |||
b0e38d7bde | |||
4d643f2cba | |||
09a56ae03c | |||
c2b5d29c6a | |||
a3d2b41b54 | |||
da679371b7 | |||
83283cdcf3 | |||
e180e2af88 | |||
14c3655ba1 | |||
f330469bc9 | |||
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 |
5
.gitattributes
vendored
5
.gitattributes
vendored
@ -4,6 +4,11 @@ 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
|
||||
.gitattributes export-ignore
|
||||
.gitmodules export-ignore
|
||||
.gitignore export-ignore
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,3 +7,5 @@ dist
|
||||
*.pyc
|
||||
*.tar.*
|
||||
_trial_temp
|
||||
deluge/i18n/*/
|
||||
*.desktop
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "libtorrent"]
|
||||
path = libtorrent
|
||||
url = git://deluge-torrent.org/libtorrent
|
797
AUTHORS
Normal file
797
AUTHORS
Normal file
@ -0,0 +1,797 @@
|
||||
Authors:
|
||||
* Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||
* Damien Churchill ('damoxc') <damoxc@gmail.com>
|
||||
|
||||
Main Developers:
|
||||
* Andrew Resch
|
||||
* Damien Churchill
|
||||
* John Garland ('johnnyg') <johnnybg+deluge@gmail.com>
|
||||
* Calum Lind ('cas') <calumlind+deluge@gmail.com>
|
||||
|
||||
libtorrent (http://www.libtorrent.org):
|
||||
* Arvid Norberg
|
||||
|
||||
Contributors (and Past Developers):
|
||||
* Zach Tibbitts <zach@collegegeek.org>
|
||||
* Alon Zakai ('Kripken') <kripkensteiner@gmail.com>
|
||||
* Marcos Pinto ('markybob') <markybob@gmail.com>
|
||||
* Alex Dedul
|
||||
* Sadrul Habib Chowdhury
|
||||
* Ido Abramovich <ido.deluge@gmail.com>
|
||||
* Martijn Voncken <mvoncken@gmail.com>
|
||||
* Mark Stahler ('kramed') <markstahler@gmail.com>
|
||||
* Pedro Algarvio ('s0undt3ch') <ufs@ufsoft.org>
|
||||
* Cristian Greco ('cgreco') <cristian@regolo.cc>
|
||||
* Chase Sterling ('gazpachoKing') <chase.sterling@gmail.com>
|
||||
|
||||
Plugin Developers:
|
||||
* Autoadd : Chase Sterling
|
||||
* Blocklist : John Garland
|
||||
* Execute : Damien Churchill
|
||||
* Extractor : Andrew Resch
|
||||
* Label : Martijn Voncken
|
||||
* Notifications : Pedro Algarvio
|
||||
* Scheduler : Andrew Resch
|
||||
* Webui : Damien Churchill
|
||||
|
||||
Images Authors:
|
||||
|
||||
* files: deluge/data/pixmaps/*.svg, *.png
|
||||
deluge/ui/web/icons/active.png, alert.png, all.png, checking.png, dht.png,
|
||||
downloading.png, inactive.png, queued.png, seeding.png, traffic.png
|
||||
exceptions: deluge/data/pixmaps/deluge.svg and derivatives
|
||||
copyright: Andrew Resch
|
||||
license: GPLv3
|
||||
|
||||
* files: deluge/data/pixmaps/deluge.svg and derivatives
|
||||
deluge/ui/web/icons/apple-pre-*.png, deluge*.png
|
||||
deluge/ui/web/images/deluge*.png
|
||||
copyright: Andrew Wedderburn
|
||||
license: GPLv3
|
||||
|
||||
* files: deluge/plugins/blocklist/blocklist/data/*.png
|
||||
deluge/data/pixmaps/tracker_warning16.png, tracker_all16.png, lock48.png
|
||||
copyright: Gnome Icon Theme
|
||||
license: GPLv2
|
||||
url: http://ftp.acc.umu.se/pub/GNOME/sources/gnome-icon-theme
|
||||
|
||||
* files: deluge/data/pixmaps/magnet.png
|
||||
copyright: Woothemes
|
||||
license: Freeware
|
||||
icon pack: WP Woothemes Ultimate
|
||||
url: http://www.woothemes.com/
|
||||
|
||||
* files: deluge/data/pixmaps/flags/*.png
|
||||
copyright: Mark James <mjames@gmail.com>
|
||||
license: Public Domain
|
||||
url: http://famfamfam.com/lab/icons/flags/
|
||||
|
||||
* files: deluge/ui/web/icons/*.png
|
||||
exceptions: apple-pre-*.png, active.png, alert.png, all.png, deluge.png, dht.png,
|
||||
downloading.png, inactive.png, queued.png, seeding.png, traffic.png
|
||||
copyright: Yusuke Kamiyamane <p@yusukekamiyamane.com>
|
||||
license: Creative Commons Attribution 3.0 License
|
||||
url: http://p.yusukekamiyamane.com/
|
||||
|
||||
* files: deluge/ui/web/images/spinner.gif, spinner-split.gif
|
||||
copyright: Steven Chim
|
||||
license: BSD license
|
||||
url: http://members.upc.nl/j.chim/ext/spinner2/ext-spinner.html
|
||||
|
||||
Translation Contributors:
|
||||
* files: deluge/i18n/*.po
|
||||
|
||||
Aaron Wang Shi
|
||||
abbigss
|
||||
ABCdatos
|
||||
Abcx
|
||||
Actam
|
||||
Adam
|
||||
adaminikisi
|
||||
adi_oporanu
|
||||
Adrian Goll
|
||||
afby
|
||||
Ahmades
|
||||
Ahmad Farghal
|
||||
Ahmad Gharbeia أحمد غربية
|
||||
akira
|
||||
Aki Sivula
|
||||
Alan Pepelko
|
||||
Alberto
|
||||
Alberto Ferrer
|
||||
alcatr4z
|
||||
AlckO
|
||||
Aleksej Korgenkov
|
||||
Alessio Treglia
|
||||
Alexander Ilyashov
|
||||
Alexander Matveev
|
||||
Alexander Saltykov
|
||||
Alexander Taubenkorb
|
||||
Alexander Telenga
|
||||
Alexander Yurtsev
|
||||
Alexandre Martani
|
||||
Alexandre Rosenfeld
|
||||
Alexandre Sapata Carbonell
|
||||
Alexey Osipov
|
||||
Alin Claudiu Radut
|
||||
allah
|
||||
AlSim
|
||||
Alvaro Carrillanca P.
|
||||
A.Matveev
|
||||
Andras Hipsag
|
||||
András Kárász
|
||||
Andrea Ratto
|
||||
Andreas Johansson
|
||||
Andreas Str
|
||||
André F. Oliveira
|
||||
AndreiF
|
||||
andrewh
|
||||
Angel Guzman Maeso
|
||||
Aníbal Deboni Neto
|
||||
animarval
|
||||
Antonio Cono
|
||||
antoniojreyes
|
||||
Anton Shestakov
|
||||
Anton Yakutovich
|
||||
antou
|
||||
Arkadiusz Kalinowski
|
||||
Artin
|
||||
artir
|
||||
Astur
|
||||
Athanasios Lefteris
|
||||
Athmane MOKRAOUI (ButterflyOfFire)
|
||||
Augusta Carla Klug
|
||||
Avoledo Marco
|
||||
axaard
|
||||
AxelRafn
|
||||
Axezium
|
||||
Ayont
|
||||
b3rx
|
||||
Bae Taegil
|
||||
Bajusz Tamás
|
||||
Balaam's Miracle
|
||||
Ballestein
|
||||
Bent Ole Fosse
|
||||
berto89
|
||||
bigx
|
||||
Bjorn Inge Berg
|
||||
blackbird
|
||||
Blackeyed
|
||||
blackmx
|
||||
BlueSky
|
||||
Blutheo
|
||||
bmhm
|
||||
bob00work
|
||||
boenki
|
||||
Bogdan Bădic-Spătariu
|
||||
bonpu
|
||||
Boone
|
||||
boss01
|
||||
Branislav Jovanović
|
||||
bronze
|
||||
brownie
|
||||
Brus46
|
||||
bumper
|
||||
butely
|
||||
BXCracer
|
||||
c0nfidencal
|
||||
Can Kaya
|
||||
Carlos Alexandro Becker
|
||||
cassianoleal
|
||||
Cédric.h
|
||||
César Rubén
|
||||
chaoswizard
|
||||
Chen Tao
|
||||
chicha
|
||||
Chien Cheng Wei
|
||||
Christian Kopac
|
||||
Christian Widell
|
||||
Christoffer Brodd-Reijer
|
||||
christooss
|
||||
CityAceE
|
||||
Clopy
|
||||
Clusty
|
||||
cnu
|
||||
Commandant
|
||||
Constantinos Koniaris
|
||||
Coolmax
|
||||
cosmix
|
||||
Costin Chirvasuta
|
||||
CoVaLiDiTy
|
||||
cow_2001
|
||||
Crispin Kirchner
|
||||
crom
|
||||
Cruster
|
||||
Cybolic
|
||||
Dan Bishop
|
||||
Danek
|
||||
Dani
|
||||
Daniel Demarco
|
||||
Daniel Ferreira
|
||||
Daniel Frank
|
||||
Daniel Holm
|
||||
Daniel Høyer Iversen
|
||||
Daniel Marynicz
|
||||
Daniel Nylander
|
||||
Daniel Patriche
|
||||
Daniel Schildt
|
||||
Daniil Sorokin
|
||||
Dante Díaz
|
||||
Daria Michalska
|
||||
DarkenCZ
|
||||
Darren
|
||||
Daspah
|
||||
David Eurenius
|
||||
davidhjelm
|
||||
David Machakhelidze
|
||||
Dawid Dziurdzia
|
||||
Daya Adianto
|
||||
dcruz
|
||||
Deady
|
||||
Dereck Wonnacott
|
||||
Devgru
|
||||
Devid Antonio FiloniDevilDogTG
|
||||
di0rz`
|
||||
Dialecti Valsamou
|
||||
Diego Medeiros
|
||||
Dkzoffy
|
||||
Dmitrij D. Czarkoff
|
||||
Dmitriy Geels
|
||||
Dmitry Olyenyov
|
||||
Dominik Kozaczko
|
||||
Dominik Lübben
|
||||
doomster
|
||||
Dorota Król
|
||||
Doyen Philippe
|
||||
Dread Knight
|
||||
DreamSonic
|
||||
duan
|
||||
Duong Thanh An
|
||||
DvoglavaZver
|
||||
dwori
|
||||
dylansmrjones
|
||||
Ebuntor
|
||||
Edgar Alejandro Jarquin Flores
|
||||
Eetu
|
||||
ekerazha
|
||||
Elias Julkunen
|
||||
elparia
|
||||
Emberke
|
||||
Emiliano Goday Caneda
|
||||
EndelWar
|
||||
eng.essam
|
||||
enubuntu
|
||||
ercangun
|
||||
Erdal Ronahi
|
||||
ergin üresin
|
||||
Eric
|
||||
Éric Lassauge
|
||||
Erlend Finvåg
|
||||
Errdil
|
||||
ethan shalev
|
||||
Evgeni Spasov
|
||||
ezekielnin
|
||||
Fabian Ordelmans
|
||||
Fabio Mazanatti
|
||||
Fábio Nogueira
|
||||
FaCuZ
|
||||
Felipe Lerena
|
||||
Fernando Pereira
|
||||
fjetland
|
||||
Florian Schäfer
|
||||
FoBoS
|
||||
Folke
|
||||
Force
|
||||
fosk
|
||||
fragarray
|
||||
freddeg
|
||||
Frédéric Perrin
|
||||
Fredrik Kilegran
|
||||
FreeAtMind
|
||||
Fulvio Ciucci
|
||||
Gabor Kelemen
|
||||
Galatsanos Panagiotis
|
||||
Gaussian
|
||||
gdevitis
|
||||
Georg Brzyk
|
||||
George Dumitrescu
|
||||
Georgi Arabadjiev
|
||||
Georg Sieber
|
||||
Gerd Radecke
|
||||
Germán Heusdens
|
||||
Gianni Vialetto
|
||||
Gigih Aji Ibrahim
|
||||
Giorgio Wicklein
|
||||
Giovanni Rapagnani
|
||||
Giuseppe
|
||||
gl
|
||||
glen
|
||||
granjerox
|
||||
Green Fish
|
||||
greentea
|
||||
Greyhound
|
||||
G. U.
|
||||
Guillaume BENOIT
|
||||
Guillaume Pelletier
|
||||
Gustavo Henrique Klug
|
||||
gutocarvalho
|
||||
Guybrush88
|
||||
Hans Rødtang
|
||||
HardDisk
|
||||
Hargas Gábor
|
||||
Heitor Thury Barreiros Barbosa
|
||||
helios91940
|
||||
helix84
|
||||
Helton Rodrigues
|
||||
Hendrik Luup
|
||||
Henrique Ferreiro
|
||||
Henry Goury-Laffont
|
||||
Hezy Amiel
|
||||
hidro
|
||||
hoball
|
||||
hokten
|
||||
Holmsss
|
||||
hristo.num
|
||||
Hubert Życiński
|
||||
Hyo
|
||||
Iarwain
|
||||
ibe
|
||||
ibear
|
||||
Id2ndR
|
||||
Igor Zubarev
|
||||
IKON (Ion)
|
||||
imen
|
||||
Ionuț Jula
|
||||
Isabelle STEVANT
|
||||
István Nyitrai
|
||||
Ivan Petrovic
|
||||
Ivan Prignano
|
||||
IvaSerge
|
||||
jackmc
|
||||
Jacks0nxD
|
||||
Jack Shen
|
||||
Jacky Yeung
|
||||
Jacques Stadler
|
||||
Janek Thomaschewski
|
||||
Jan Kaláb
|
||||
Jan Niklas Hasse
|
||||
Jasper Groenewegen
|
||||
Javi Rodríguez
|
||||
Jayasimha (ಜಯಸಿಂಹ)
|
||||
jeannich
|
||||
Jeff Bailes
|
||||
Jesse Zilstorff
|
||||
Joan Duran
|
||||
João Santos
|
||||
Joar Bagge
|
||||
Joe Anderson
|
||||
Joel Calado
|
||||
Johan Linde
|
||||
John Garland
|
||||
Jojan
|
||||
jollyr0ger
|
||||
Jonas Bo Grimsgaard
|
||||
Jonas Granqvist
|
||||
Jonas Slivka
|
||||
Jonathan Zeppettini
|
||||
Jørgen
|
||||
Jørgen Tellnes
|
||||
josé
|
||||
José Geraldo Gouvêa
|
||||
José Iván León Islas
|
||||
José Lou C.
|
||||
Jose Sun
|
||||
Jr.
|
||||
Jukka Kauppinen
|
||||
Julián Alarcón
|
||||
julietgolf
|
||||
Jusic
|
||||
Justzupi
|
||||
Kaarel
|
||||
Kai Thomsen
|
||||
Kalman Tarnay
|
||||
Kamil Páral
|
||||
Kane_F
|
||||
kaotiks@gmail.com
|
||||
Kateikyoushii
|
||||
kaxhinaz
|
||||
Kazuhiro NISHIYAMA
|
||||
Kerberos
|
||||
Keresztes Ákos
|
||||
kevintyk
|
||||
kiersie
|
||||
Kimbo^
|
||||
Kim Lübbe
|
||||
kitzOgen
|
||||
Kjetil Rydland
|
||||
kluon
|
||||
kmikz
|
||||
Knedlyk
|
||||
koleoptero
|
||||
Kőrösi Krisztián
|
||||
Kouta
|
||||
Krakatos
|
||||
Krešo Kunjas
|
||||
kripken
|
||||
Kristaps
|
||||
Kristian Øllegaard
|
||||
Kristoffer Egil Bonarjee
|
||||
Krzysztof Janowski
|
||||
Krzysztof Zawada
|
||||
Larry Wei Liu
|
||||
laughterwym
|
||||
Laur Mõtus
|
||||
lazka
|
||||
leandrud
|
||||
lê bình
|
||||
Le Coz Florent
|
||||
Leo
|
||||
liorda
|
||||
LKRaider
|
||||
LoLo_SaG
|
||||
Long Tran
|
||||
Lorenz
|
||||
Low Kian Seong
|
||||
Luca Andrea Rossi
|
||||
Luca Ferretti
|
||||
Lucky LIX
|
||||
Luis Gomes
|
||||
Luis Reis
|
||||
Łukasz Wyszyński
|
||||
luojie-dune
|
||||
maaark
|
||||
Maciej Chojnacki
|
||||
Maciej Meller
|
||||
Mads Peter Rommedahl
|
||||
Major Kong
|
||||
Malaki
|
||||
malde
|
||||
Malte Lenz
|
||||
Mantas Kriaučiūnas
|
||||
Mara Sorella
|
||||
Marcin
|
||||
Marcin Falkiewicz
|
||||
marcobra
|
||||
Marco da Silva
|
||||
Marco de Moulin
|
||||
Marco Rodrigues
|
||||
Marcos
|
||||
Marcos Escalier
|
||||
Marcos Pinto
|
||||
Marcus Ekstrom
|
||||
Marek Dębowski
|
||||
Mário Buči
|
||||
Mario Munda
|
||||
Marius Andersen
|
||||
Marius Hudea
|
||||
Marius Mihai
|
||||
Mariusz Cielecki
|
||||
Mark Krapivner
|
||||
marko-markovic
|
||||
Markus Brummer
|
||||
Markus Sutter
|
||||
Martin
|
||||
Martin Dybdal
|
||||
Martin Iglesias
|
||||
Martin Lettner
|
||||
Martin Pihl
|
||||
Masoud Kalali
|
||||
mat02
|
||||
Matej Urbančič
|
||||
Mathias-K
|
||||
Mathieu Arès
|
||||
Mathieu D. (MatToufoutu)
|
||||
Mathijs
|
||||
Matrik
|
||||
Matteo Renzulli
|
||||
Matteo Settenvini
|
||||
Matthew Gadd
|
||||
Matthias Benkard
|
||||
Matthias Mailänder
|
||||
Mattias Ohlsson
|
||||
Mauro de Carvalho
|
||||
Max Molchanov
|
||||
Me
|
||||
MercuryCC
|
||||
Mert Bozkurt
|
||||
Mert Dirik
|
||||
MFX
|
||||
mhietar
|
||||
mibtha
|
||||
Michael Budde
|
||||
Michael Kaliszka
|
||||
Michalis Makaronides
|
||||
Michał Tokarczyk
|
||||
Miguel Pires da Rosa
|
||||
Mihai Capotă
|
||||
Miika Metsälä
|
||||
Mikael Fernblad
|
||||
Mike Sierra
|
||||
mikhalek
|
||||
Milan Prvulović
|
||||
Milo Casagrande
|
||||
Mindaugas
|
||||
Miroslav Matejaš
|
||||
misel
|
||||
mithras
|
||||
Mitja Pagon
|
||||
M.Kitchen
|
||||
Mohamed Magdy
|
||||
moonkey
|
||||
MrBlonde
|
||||
muczy
|
||||
Münir Ekinci
|
||||
Mustafa Temizel
|
||||
mvoncken
|
||||
Mytonn
|
||||
NagyMarton
|
||||
neaion
|
||||
Neil Lin
|
||||
Nemo
|
||||
Nerijus Arlauskas
|
||||
Nicklas Larsson
|
||||
Nicolaj Wyke
|
||||
Nicola Piovesan
|
||||
Nicolas Sabatier
|
||||
Nicolas Velin
|
||||
Nightfall
|
||||
NiKoB
|
||||
Nikolai M. Riabov
|
||||
Niko_Thien
|
||||
niska
|
||||
Nithir
|
||||
noisemonkey
|
||||
nomemohes
|
||||
nosense
|
||||
null
|
||||
Nuno Estêvão
|
||||
Nuno Santos
|
||||
nxxs
|
||||
nyo
|
||||
obo
|
||||
Ojan
|
||||
Olav Andreas Lindekleiv
|
||||
oldbeggar
|
||||
Olivier FAURAX
|
||||
orphe
|
||||
osantana
|
||||
Osman Tosun
|
||||
OssiR
|
||||
otypoks
|
||||
ounn
|
||||
Oz123
|
||||
Özgür BASKIN
|
||||
Pablo Carmona A.
|
||||
Pablo Ledesma
|
||||
Pablo Navarro Castillo
|
||||
Paco Molinero
|
||||
Pål-Eivind Johnsen
|
||||
pano
|
||||
Paolo Naldini
|
||||
Paracelsus
|
||||
Patryk13_03
|
||||
Patryk Skorupa
|
||||
PattogoTehen
|
||||
Paul Lange
|
||||
Pavcio
|
||||
Paweł Wysocki
|
||||
Pedro Brites Moita
|
||||
Pedro Clemente Pereira Neto
|
||||
Pekka "PEXI" Niemistö
|
||||
Penegal
|
||||
Penzo
|
||||
perdido
|
||||
Peter Kotrcka
|
||||
Peter Skov
|
||||
Peter Van den Bosch
|
||||
Petter Eklund
|
||||
Petter Viklund
|
||||
phatsphere
|
||||
Phenomen
|
||||
Philipi
|
||||
Philippides Homer
|
||||
phoenix
|
||||
pidi
|
||||
Pierre Quillery
|
||||
Pierre Rudloff
|
||||
Pierre Slamich
|
||||
Pietrao
|
||||
Piotr Strębski
|
||||
Piotr Wicijowski
|
||||
Pittmann Tamás
|
||||
Playmolas
|
||||
Prescott
|
||||
Prescott_SK
|
||||
pronull
|
||||
Przemysław Kulczycki
|
||||
Pumy
|
||||
pushpika
|
||||
PY
|
||||
qubicllj
|
||||
r21vo
|
||||
Rafał Barański
|
||||
rainofchaos
|
||||
Rajbir
|
||||
ras0ir
|
||||
Rat
|
||||
rd1381
|
||||
Renato
|
||||
Rene Hennig
|
||||
Rene Pärts
|
||||
Ricardo Duarte
|
||||
Richard
|
||||
Robert Hrovat
|
||||
Roberth Sjonøy
|
||||
Robert Lundmark
|
||||
Robin Jakobsson
|
||||
Robin Kåveland
|
||||
Rodrigo Donado
|
||||
Roel Groeneveld
|
||||
rohmaru
|
||||
Rolf Christensen
|
||||
Rolf Leggewie
|
||||
Roni Kantis
|
||||
Ronmi
|
||||
Rostislav Raykov
|
||||
royto
|
||||
RuiAmaro
|
||||
Rui Araújo
|
||||
Rui Moura
|
||||
Rune Svendsen
|
||||
Rusna
|
||||
Rytis
|
||||
Sabirov Mikhail
|
||||
salseeg
|
||||
Sami Koskinen
|
||||
Samir van de Sand
|
||||
Samuel Arroyo Acuña
|
||||
Samuel R. C. Vale
|
||||
Sanel
|
||||
Santi
|
||||
Santi Martínez Cantelli
|
||||
Sardan
|
||||
Sargate Kanogan
|
||||
Sarmad Jari
|
||||
Saša Bodiroža
|
||||
sat0shi
|
||||
Saulius Pranckevičius
|
||||
Savvas Radevic
|
||||
Sebastian Krauß
|
||||
Sebastián Porta
|
||||
Sedir
|
||||
Sefa Denizoğlu
|
||||
sekolands
|
||||
Selim Suerkan
|
||||
semsomi
|
||||
Sergii Golovatiuk
|
||||
setarcos
|
||||
Sheki
|
||||
Shironeko
|
||||
Shlomil
|
||||
silfiriel
|
||||
Simone Tolotti
|
||||
Simone Vendemia
|
||||
sirkubador
|
||||
Sławomir Więch
|
||||
slip
|
||||
slyon
|
||||
smoke
|
||||
Sonja
|
||||
spectral
|
||||
spin_555
|
||||
spitf1r3
|
||||
Spiziuz
|
||||
Spyros Theodoritsis
|
||||
SqUe
|
||||
Squigly
|
||||
srtck
|
||||
Stefan Horning
|
||||
Stefano Maggiolo
|
||||
Stefano Roberto Soleti
|
||||
steinberger
|
||||
Stéphane Travostino
|
||||
Stephan Klein
|
||||
Steven De Winter
|
||||
Stevie
|
||||
Stian24
|
||||
stylius
|
||||
Sukarn Maini
|
||||
Sunjae Park
|
||||
Susana Pereira
|
||||
szymon siglowy
|
||||
takercena
|
||||
TAS
|
||||
Taygeto
|
||||
temy4
|
||||
texxxxxx
|
||||
thamood
|
||||
Thanos Chatziathanassiou
|
||||
Tharawut Paripaiboon
|
||||
Theodoor
|
||||
Théophane Anestis
|
||||
Thor Marius K. Høgås
|
||||
Tiago Silva
|
||||
Tiago Sousa
|
||||
Tikkel
|
||||
tim__b
|
||||
Tim Bordemann
|
||||
Tim Fuchs
|
||||
Tim Kornhammar
|
||||
Timo
|
||||
Timo Jyrinki
|
||||
Timothy Babych
|
||||
TitkosRejtozo
|
||||
Tom
|
||||
Tomas Gustavsson
|
||||
Tomas Valentukevičius
|
||||
Tomasz Dominikowski
|
||||
Tomislav Plavčić
|
||||
Tom Mannerhagen
|
||||
Tommy Mikkelsen
|
||||
Tom Verdaat
|
||||
Tony Manco
|
||||
Tor Erling H. Opsahl
|
||||
Toudi
|
||||
tqm_z
|
||||
Trapanator
|
||||
Tribaal
|
||||
Triton
|
||||
TuniX12
|
||||
Tuomo Sipola
|
||||
turbojugend_gr
|
||||
Turtle.net
|
||||
twilight
|
||||
tymmej
|
||||
Ulrik
|
||||
Umarzuki Mochlis
|
||||
unikob
|
||||
Vadim Gusev
|
||||
Vagi
|
||||
Valentin Bora
|
||||
Valmantas Palikša
|
||||
VASKITTU
|
||||
Vassilis Skoullis
|
||||
vetal17
|
||||
vicedo
|
||||
viki
|
||||
villads hamann
|
||||
Vincent Garibal
|
||||
Vincent Ortalda
|
||||
vinchi007
|
||||
Vinícius de Figueiredo Silva
|
||||
Vinzenz Vietzke
|
||||
virtoo
|
||||
virtual_spirit
|
||||
Vitor Caike
|
||||
Vitor Lamas Gatti
|
||||
Vladimir Lazic
|
||||
Vladimir Sharshov
|
||||
Wanderlust
|
||||
Wander Nauta
|
||||
Ward De Ridder
|
||||
WebCrusader
|
||||
webdr
|
||||
Wentao Tang
|
||||
wilana
|
||||
Wilfredo Ernesto Guerrero Campos
|
||||
Wim Champagne
|
||||
World Sucks
|
||||
Xabi Ezpeleta
|
||||
Xavi de Moner
|
||||
XavierToo
|
||||
XChesser
|
||||
Xiaodong Xu
|
||||
xyb
|
||||
Yaron
|
||||
Yasen Pramatarov
|
||||
YesPoX
|
||||
Yuren Ju
|
||||
Yves MATHIEU
|
||||
zekopeko
|
||||
zhuqin
|
||||
Zissan
|
||||
Γιάννης Κατσαμπίρης
|
||||
Артём Попов
|
||||
Миша
|
||||
Шаймарданов Максим
|
||||
蔡查理
|
249
ChangeLog
249
ChangeLog
@ -1,4 +1,231 @@
|
||||
=== Deluge 1.3.0 (In Development) ===
|
||||
=== Deluge 1.3.4 (03 March 2012) ===
|
||||
==== Core ====
|
||||
* #1921: Free disk space reporting incorrectly in FreeBSD
|
||||
* #1964: Fix unhandled UnpicklingErrors
|
||||
* #1967: Fix unhandled IndexError when trying to open a non-json conf file
|
||||
* Fix setting daemon listen interface from command line
|
||||
* #2021: Fix share ratio limit not obeyed for seeded torrents added to session
|
||||
* Add optparse custom version to prevent unnecessary loading of libtorrent
|
||||
* #1554: Fix seeding on share ratio failing for partially selected torrents
|
||||
* Add proper process title naming in ps, top etc. (Depends: setproctitle)
|
||||
|
||||
==== GtkUI ====
|
||||
* #1918: Fix Drag'n'Drop not working in Windows
|
||||
* #1941: Increase maximum Cache Size to 999999 (15GiB)
|
||||
* #1940: File & folder renaming issue when using Add Torrent dialog in Windows
|
||||
* LP#821577: Fix UnpicklingError when external selection dragged onto Files Tab
|
||||
* #1934: Fix Unicode error in AddTorrent Dialog
|
||||
* #1957: Fix keyerror when adding columns for non-latin languages
|
||||
* #1969: Fix menu item 'Quit & Shutdown' still available when not connected to daemon
|
||||
* #1895: Fix Files Tab showing wrong files due to torrent_info race condition
|
||||
* #2010: Move speed text in titlebar to the beginning
|
||||
* #2032: Wait for client to shutdown/disconnect before stopping reactor
|
||||
* Fix compatibility with Python 2.5
|
||||
* Fix collapsed treeview in Create Torrent dialog
|
||||
* Ignore unmaximise event when window isn't visible
|
||||
* #1976: Fixed text entry with trailing newline characters causing issues for Move Storage
|
||||
|
||||
==== WebUI ====
|
||||
* Fix Webui files-tab menu setting wrong priority
|
||||
* Update to ExtJS 3.4.0
|
||||
* #1960: Fix statustab showing total_payload_download for upload as well
|
||||
* Remove uneeded Titlebar to save space
|
||||
* Fix clipped Browse button in WebUI
|
||||
* #1915: Fix being unable to stop the status bar from autohiding
|
||||
* Fix password box focus issue in Firefox
|
||||
* Fix plugin uploads from behind a reverse proxy
|
||||
* #2010: Move speed text in titlebar to the beginning
|
||||
* #1936: Fix Referenced before assignment error in json_api
|
||||
* Changes are now applied when clicking OK in Preferences
|
||||
* Added Download,Uploaded,Down Limit, Up Limit & Seeder/Peeds columns
|
||||
* Add magnet uri support to Add Url
|
||||
* Add keymaps for torrents - Ctrl-A (select all) and Delete
|
||||
* #2037: Fix 'Add Torrents' torrents list not scrolling
|
||||
* #2038: Fix Chrome 17 disconnecting from webui
|
||||
|
||||
==== Console ====
|
||||
* #1953: Fix flickering on every update
|
||||
* #1954: Fix 'invalid literal for float' when setting listen interface
|
||||
* #1945: Fix UnicodeDecodeError when using non-ascii chars in info
|
||||
|
||||
==== Label ====
|
||||
* #1961: Add missing 'All' filter option
|
||||
* #2035: Fix label options dialog in webui
|
||||
* #2036: Fix newly added labels not being sorted in torrent right click menu
|
||||
|
||||
==== Notification ====
|
||||
* #1905: Fix no email sent to second email address
|
||||
* #1898: Fix email notifications not including date/time they were sent
|
||||
|
||||
==== Scheduler ====
|
||||
* Add plugin page for WebUi
|
||||
|
||||
==== Execute ====
|
||||
* Commands now run scripts asynchronous to prevent Deluge from hanging
|
||||
|
||||
==== AutoAdd ====
|
||||
* Added watch folder support for '.magnet' text file containing single or multiple magnet uris
|
||||
* Fix glade object issue when re-enabling plugin in same session
|
||||
* Fix plugin not showing as enabled in webui
|
||||
|
||||
=== Deluge 1.3.3 (22 July 2011) ===
|
||||
==== Core ====
|
||||
* Properly show the 'Checking Resume Data' state instead of just 7
|
||||
* #1788: Added ability to use XDG_DOWNLOAD_DIR as default download folder
|
||||
* Fix path error with torrent files prefixed with 'file://' from Firefox
|
||||
* #1869: Fix setting the disk io read/write to bypass OS cache in Windows
|
||||
* #1504: Fix win32 running deluged as not logged in user via runas or service
|
||||
* #890: If added torrent already exists, append extra trackers to it
|
||||
* #1338: Fix Seeds and Peers totals not updating
|
||||
* #1239: Fix translated Tracker Error text not counted in sidebar Error status
|
||||
* Fix httpdownloader error with existing filename
|
||||
* #1505: Add libtorrent info to version output
|
||||
* #1637 Fix UnicodeDecodeError from 'deluge-* --help' with non-english languages
|
||||
* #1714 Fix handling of backslashes when renaming files/folders
|
||||
|
||||
==== GtkUI ====
|
||||
* Show the checking icon for torrents in the 'Checking Resume Data' state
|
||||
* #1195: Fix right-click selecting issue when switching between folders and files
|
||||
* Add F2 key shortcut for renaming filenames in the Files Tab
|
||||
* Increase max piece size to 16 MiB in create torrent dialog
|
||||
* #1475: Fix save and restore Preferences dialog size from config
|
||||
* Add search as you type to the torrent view
|
||||
* #1456: Fix no ETA showing with multiple files
|
||||
* #1560: Fix FilesTab Progress value sorting by int instead of float
|
||||
* #1263: Fix not remembering column widths
|
||||
* #948: New Release Dialog now shows the server version
|
||||
* Fix peers in PeersTab showing non-zero download rate when seeding
|
||||
|
||||
==== AutoAdd ====
|
||||
* #1861: Fix AutoAdd Warning (column number is a boolean)
|
||||
|
||||
==== Label ====
|
||||
* #1246: Fix losing Labels upon restart
|
||||
|
||||
==== Execute ====
|
||||
* #1477: Fix ignore Added events from state file on startup
|
||||
|
||||
==== ConsoleUI ====
|
||||
* #1258: Add support for urls and magnet uris in add command
|
||||
* #1801: Fix unhandled defered error and missing error message upon failed connect
|
||||
|
||||
=== 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 +234,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 ====
|
||||
|
6
DEPENDS
6
DEPENDS
@ -6,8 +6,11 @@
|
||||
* simplejson (if python < 2.6)
|
||||
* setuptools
|
||||
* gettext
|
||||
* intltool
|
||||
* pyxdg
|
||||
* chardet
|
||||
* geoip-database (optional)
|
||||
* setproctitle (optional)
|
||||
|
||||
* libtorrent >= 0.14, or build the included version
|
||||
|
||||
@ -16,9 +19,6 @@
|
||||
* openssl
|
||||
* zlib
|
||||
|
||||
=== UIs ===
|
||||
* chardet
|
||||
|
||||
=== Gtk ===
|
||||
* python-notify (libnotify python wrapper)
|
||||
* pygame
|
||||
|
69
README
69
README
@ -1,4 +1,3 @@
|
||||
|
||||
==========================
|
||||
Deluge BitTorrent Client
|
||||
==========================
|
||||
@ -9,86 +8,50 @@ Authors:
|
||||
Andrew Resch
|
||||
Damien Churchill
|
||||
|
||||
For past developers and contributers see: http://dev.deluge-torrent.org/wiki/About
|
||||
|
||||
==========================
|
||||
License
|
||||
==========================
|
||||
Deluge is under the GNU GPLv3 license.
|
||||
Icon data/pixmaps/deluge.svg and derivatives in data/icons are copyright
|
||||
Andrew Wedderburn and are under the GNU GPLv3.
|
||||
All other icons in data/pixmaps are copyright Andrew Resch and are under
|
||||
the GNU GPLv3.
|
||||
|
||||
==========================
|
||||
Contact/Support:
|
||||
==========================
|
||||
|
||||
We have two options available for support:
|
||||
|
||||
Our Forum, at: http://forum.deluge-torrent.org
|
||||
or
|
||||
Our IRC Channel, at #deluge on Freenode: http://freenode.net
|
||||
For contributors and past developers see:
|
||||
AUTHORS
|
||||
|
||||
==========================
|
||||
Installation Instructions:
|
||||
==========================
|
||||
|
||||
For more detailed instructions see: http://dev.deluge-torrent.org/wiki/Installing/Source
|
||||
For detailed instructions see: http://dev.deluge-torrent.org/wiki/Installing/Source
|
||||
|
||||
See: DEPENDS for a full list of dependencies.
|
||||
Ensure build dependencies are installed, see DEPENDS for a full listing.
|
||||
|
||||
First, make sure you have the proper build dependencies installed. On a normal
|
||||
Debian or Ubuntu system:
|
||||
|
||||
sudo apt-get install g++ make python-all-dev python-all python-dbus \
|
||||
python-gtk2 python-notify librsvg2-common python-xdg python-support \
|
||||
subversion libboost-dev libboost-python-dev \
|
||||
libboost-thread-dev libboost-date-time-dev libboost-filesystem-dev \
|
||||
libssl-dev zlib1g-dev python-setuptools \
|
||||
python-mako python-twisted-web python-chardet python-simplejson
|
||||
|
||||
The names of the packages may vary depending on your OS / distro.
|
||||
|
||||
Once you have the needed libraries installed, build and install by running:
|
||||
Build and install by running:
|
||||
|
||||
$ python setup.py build
|
||||
$ sudo python setup.py install
|
||||
|
||||
==========================
|
||||
Contact/Support:
|
||||
==========================
|
||||
|
||||
Forum: http://forum.deluge-torrent.org
|
||||
IRC Channel: #deluge on irc.freenode.net
|
||||
|
||||
==========================
|
||||
FAQ
|
||||
==========================
|
||||
|
||||
For the full FAQ see: http://dev.deluge-torrent.org/wiki/Faq
|
||||
|
||||
How to start the various user-interfaces
|
||||
|
||||
Gtk:
|
||||
deluge-gtk
|
||||
deluge or deluge-gtk
|
||||
Console:
|
||||
deluge-console
|
||||
Web:
|
||||
deluge-web
|
||||
Go to http://localhost:8112/ default-password = "deluge"
|
||||
|
||||
Why is deluge still listed in my system tray even after I close it ?
|
||||
|
||||
You closed the gtk user-interface but you did not close the daemon. Choose "Quit & Shutdown Daemon" to close both Daemon and gtk-ui.
|
||||
|
||||
How do I start the daemon?
|
||||
|
||||
deluged
|
||||
|
||||
How do I start the daemon with logging to console?
|
||||
|
||||
deluged -d -L <log level>
|
||||
|
||||
I can't connect to the daemon from another machine
|
||||
|
||||
* Configure the daemon to allow remote connections
|
||||
* Add a user to the auth file located in the config folder: ~/.config/deluge/auth
|
||||
* Restart the daemon.
|
||||
See: http://dev.deluge-torrent.org/wiki/UserGuide/ThinClient
|
||||
|
||||
I upgraded from 0.5 and plugin x is missing
|
||||
|
||||
1.0 is a rewrite, all old 0.5 plugins have to be rewritten.
|
||||
|
||||
For the full FAQ see: http://dev.deluge-torrent.org/wiki/Faq
|
||||
|
21
check_glade.sh
Executable file
21
check_glade.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Fixes glade files which may have set gtk stock labels set to translatable
|
||||
for x in `find . -name '*.glade' |grep -v '.git\|build'` ; do \
|
||||
for y in gtk-add gtk-apply gtk-bold gtk-cancel gtk-cdrom gtk-clear \
|
||||
gtk-close gtk-color-picker gtk-connect gtk-convert gtk-copy gtk-cut \
|
||||
gtk-delete gtk-dialog-error gtk-dialog-info gtk-dialog-question \
|
||||
gtk-dialog-warning gtk-dnd gtk-dnd-multiple gtk-edit gtk-execute gtk-find \
|
||||
gtk-find-and-replace gtk-floppy gtk-goto-bottom gtk-goto-first \
|
||||
gtk-goto-last gtk-goto-top gtk-go-back gtk-go-down gtk-go-forward \
|
||||
gtk-go-up gtk-help gtk-home gtk-index gtk-italic gtk-jump-to \
|
||||
gtk-justify-center gtk-justify-fill gtk-justify-left gtk-missing-image \
|
||||
gtk-new gtk-no gtk-ok gtk-open gtk-paste gtk-preferences gtk-print \
|
||||
gtk-print-preview gtk-properties gtk-quit gtk-redo gtk-refresh \
|
||||
gtk-remove gtk-revert-to-saved gtk-save gtk-save-as gtk-select-color \
|
||||
gtk-select-font gtk-sort-descending gtk-spell-check gtk-stop \
|
||||
gtk-strikethrough gtk-undelete gtk-underline gtk-undo gtk-yes \
|
||||
gtk-zoom-100 gtk-zoom-fit gtk-zoom-in gtk-zoom-out; do \
|
||||
sed -i "s/<property\ name\=\"label\"\ translatable\=\"yes\">$y<\/property>/<property\ name\=\"label\"\ translatable\=\"no\">$y<\/property>/g" $x; \
|
||||
done;\
|
||||
done
|
17
create_potfiles_in.py
Normal file → Executable file
17
create_potfiles_in.py
Normal file → Executable file
@ -1,17 +1,26 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
|
||||
import re
|
||||
import sys
|
||||
# Paths to exclude
|
||||
EXCLUSIONS = [
|
||||
"deluge/scripts"
|
||||
"deluge/scripts",
|
||||
"deluge/i18n",
|
||||
]
|
||||
|
||||
POTFILE_IN = "deluge/i18n/POTFILES.in"
|
||||
|
||||
print "Creating " + POTFILE_IN + " .."
|
||||
pattern = "deluge\/plugins\/.*\/build"
|
||||
compiled = re.compile(pattern)
|
||||
|
||||
sys.stdout.write("Creating " + POTFILE_IN + " ... ")
|
||||
sys.stdout.flush()
|
||||
to_translate = []
|
||||
for (dirpath, dirnames, filenames) in os.walk("deluge"):
|
||||
for filename in filenames:
|
||||
if os.path.splitext(filename)[1] in (".py", ".glade") and dirpath not in EXCLUSIONS:
|
||||
if os.path.splitext(filename)[1] in (".py", ".glade", ".in") \
|
||||
and dirpath not in EXCLUSIONS \
|
||||
and not compiled.match(dirpath):
|
||||
to_translate.append(os.path.join(dirpath, filename))
|
||||
|
||||
f = open(POTFILE_IN, "wb")
|
||||
|
140
deluge/common.py
140
deluge/common.py
@ -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
|
||||
|
||||
@ -73,9 +73,7 @@ try:
|
||||
locale.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n"))
|
||||
if hasattr(locale, "textdomain"):
|
||||
locale.textdomain("deluge")
|
||||
gettext.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n"))
|
||||
gettext.textdomain("deluge")
|
||||
gettext.install("deluge", pkg_resources.resource_filename("deluge", "i18n"))
|
||||
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!")
|
||||
@ -119,11 +117,12 @@ FILE_PRIORITY = {
|
||||
0: "Do Not Download",
|
||||
1: "Normal Priority",
|
||||
2: "High Priority",
|
||||
5: "Highest Priority",
|
||||
5: "High Priority",
|
||||
7: "Highest Priority",
|
||||
"Do Not Download": 0,
|
||||
"Normal Priority": 1,
|
||||
"High Priority": 2,
|
||||
"Highest Priority": 5
|
||||
"High Priority": 5,
|
||||
"Highest Priority": 7
|
||||
}
|
||||
|
||||
def get_version():
|
||||
@ -145,11 +144,19 @@ def get_default_config_dir(filename=None):
|
||||
|
||||
"""
|
||||
if windows_check():
|
||||
appDataPath = os.environ.get("APPDATA")
|
||||
if not appDataPath:
|
||||
import _winreg
|
||||
hkey = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders")
|
||||
appDataReg = _winreg.QueryValueEx(hkey, "AppData")
|
||||
appDataPath = appDataReg[0]
|
||||
_winreg.CloseKey(hkey)
|
||||
if filename:
|
||||
return os.path.join(os.environ.get("APPDATA"), "deluge", filename)
|
||||
return os.path.join(appDataPath, "deluge", filename)
|
||||
else:
|
||||
return os.path.join(os.environ.get("APPDATA"), "deluge")
|
||||
return os.path.join(appDataPath, "deluge")
|
||||
else:
|
||||
import xdg.BaseDirectory
|
||||
if filename:
|
||||
return os.path.join(xdg.BaseDirectory.save_config_path("deluge"), filename)
|
||||
else:
|
||||
@ -162,8 +169,20 @@ def get_default_download_dir():
|
||||
|
||||
"""
|
||||
if windows_check():
|
||||
return os.path.expanduser("~")
|
||||
return os.path.join(os.path.expanduser("~"), 'Downloads')
|
||||
else:
|
||||
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
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
return os.environ.get("HOME")
|
||||
|
||||
def windows_check():
|
||||
@ -259,6 +278,30 @@ def fsize(fsize_b):
|
||||
fsize_gb = fsize_mb / 1024.0
|
||||
return "%.1f %s" % (fsize_gb, _("GiB"))
|
||||
|
||||
def fsize_short(fsize_b):
|
||||
"""
|
||||
Formats the bytes value into a string with K, M or G units
|
||||
|
||||
:param fsize_b: the filesize in bytes
|
||||
:type fsize_b: int
|
||||
:returns: formatted string in K, M or G units
|
||||
:rtype: string
|
||||
|
||||
**Usage**
|
||||
|
||||
>>> fsize(112245)
|
||||
'109.6 K'
|
||||
|
||||
"""
|
||||
fsize_kb = fsize_b / 1024.0
|
||||
if fsize_kb < 1024:
|
||||
return "%.1f %s" % (fsize_kb, _("K"))
|
||||
fsize_mb = fsize_kb / 1024.0
|
||||
if fsize_mb < 1024:
|
||||
return "%.1f %s" % (fsize_mb, _("M"))
|
||||
fsize_gb = fsize_mb / 1024.0
|
||||
return "%.1f %s" % (fsize_gb, _("G"))
|
||||
|
||||
def fpcnt(dec):
|
||||
"""
|
||||
Formats a string to display a percentage with two decimal places
|
||||
@ -291,7 +334,14 @@ def fspeed(bps):
|
||||
'42.1 KiB/s'
|
||||
|
||||
"""
|
||||
return '%s/s' % (fsize(bps))
|
||||
fspeed_kb = bps / 1024.0
|
||||
if fspeed_kb < 1024:
|
||||
return "%.1f %s" % (fspeed_kb, _("KiB/s"))
|
||||
fspeed_mb = fspeed_kb / 1024.0
|
||||
if fspeed_mb < 1024:
|
||||
return "%.1f %s" % (fspeed_mb, _("MiB/s"))
|
||||
fspeed_gb = fspeed_mb / 1024.0
|
||||
return "%.1f %s" % (fspeed_gb, _("GiB/s"))
|
||||
|
||||
def fpeer(num_peers, total_peers):
|
||||
"""
|
||||
@ -474,8 +524,8 @@ def free_space(path):
|
||||
sectors, bytes, free, total = map(long, win32file.GetDiskFreeSpace(path))
|
||||
return (free * sectors * bytes)
|
||||
else:
|
||||
disk_data = os.statvfs(path)
|
||||
block_size = disk_data.f_bsize
|
||||
disk_data = os.statvfs(path.encode("utf8"))
|
||||
block_size = disk_data.f_frsize
|
||||
return disk_data.f_bavail * block_size
|
||||
|
||||
def is_ip(ip):
|
||||
@ -560,6 +610,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 +655,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)]:
|
||||
if vs[1].startswith(("rc", "alpha", "beta")):
|
||||
self.suffix = vs[1]
|
||||
if vs[-1] == 'dev':
|
||||
self.dev = True
|
||||
|
||||
def __cmp__(self, ver):
|
||||
"""
|
||||
@ -587,19 +674,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)
|
||||
|
@ -96,6 +96,10 @@ class Component(object):
|
||||
self._component_stopping_deferred = None
|
||||
_ComponentRegistry.register(self)
|
||||
|
||||
def __del__(self):
|
||||
if _ComponentRegistry:
|
||||
_ComponentRegistry.deregister(self._component_name)
|
||||
|
||||
def _component_start_timer(self):
|
||||
if hasattr(self, "update"):
|
||||
self._component_timer = LoopingCall(self.update)
|
||||
@ -139,11 +143,18 @@ class Component(object):
|
||||
self._component_timer.stop()
|
||||
return True
|
||||
|
||||
def on_stop_fail(result):
|
||||
self._component_state = "Started"
|
||||
self._component_stopping_deferred = None
|
||||
log.error(result)
|
||||
return result
|
||||
|
||||
if self._component_state != "Stopped" and self._component_state != "Stopping":
|
||||
if hasattr(self, "stop"):
|
||||
self._component_state = "Stopping"
|
||||
d = maybeDeferred(self.stop)
|
||||
d.addCallback(on_stop)
|
||||
d.addErrback(on_stop_fail)
|
||||
self._component_stopping_deferred = d
|
||||
else:
|
||||
d = maybeDeferred(on_stop, None)
|
||||
@ -192,6 +203,18 @@ class Component(object):
|
||||
d.addCallback(on_stop)
|
||||
return d
|
||||
|
||||
def start(self):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def update(self):
|
||||
pass
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
class ComponentRegistry(object):
|
||||
"""
|
||||
The ComponentRegistry holds a list of currently registered
|
||||
|
@ -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,6 +205,9 @@ 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:
|
||||
if oldtype == unicode:
|
||||
value = oldtype(value, "utf8")
|
||||
else:
|
||||
value = oldtype(value)
|
||||
except ValueError:
|
||||
log.warning("Type '%s' invalid for '%s'", newtype, key)
|
||||
@ -250,6 +258,12 @@ what is currently in the config and it could not convert the value
|
||||
5
|
||||
|
||||
"""
|
||||
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):
|
||||
@ -396,15 +410,13 @@ 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
|
||||
if self._save_timer and self._save_timer.active():
|
||||
self._save_timer.cancel()
|
||||
return
|
||||
except Exception, e:
|
||||
log.warning("Unable to open config file: %s", filename)
|
||||
|
||||
|
||||
return True
|
||||
except (IOError, IndexError), 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:
|
||||
@ -415,7 +427,7 @@ what is currently in the config and it could not convert the value
|
||||
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
|
||||
|
||||
@ -424,7 +436,7 @@ what is currently in the config and it could not convert the value
|
||||
log.debug("Backing up old config file to %s~", filename)
|
||||
shutil.move(filename, filename + "~")
|
||||
except Exception, e:
|
||||
log.error("Error backing up old config..")
|
||||
log.warning("Unable to backup old config...")
|
||||
|
||||
# The new config file has been written successfully, so let's move it over
|
||||
# the existing one.
|
||||
|
@ -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)
|
||||
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
|
||||
@ -92,6 +95,11 @@ class Core(component.Component):
|
||||
|
||||
# Set session settings
|
||||
self.settings.send_redundant_have = True
|
||||
if deluge.common.windows_check():
|
||||
self.settings.disk_io_write_mode = \
|
||||
lt.io_buffer_mode_t.disable_os_cache
|
||||
self.settings.disk_io_read_mode = \
|
||||
lt.io_buffer_mode_t.disable_os_cache
|
||||
self.session.set_settings(self.settings)
|
||||
|
||||
# Load metadata extension
|
||||
@ -128,9 +136,12 @@ class Core(component.Component):
|
||||
self.__new_release = None
|
||||
|
||||
def stop(self):
|
||||
log.debug("Core stopping...")
|
||||
|
||||
# Save the DHT state if necessary
|
||||
if self.config["dht"]:
|
||||
self.save_dht_state()
|
||||
|
||||
# Save the libtorrent session state
|
||||
self.__save_session_state()
|
||||
|
||||
@ -147,8 +158,9 @@ class Core(component.Component):
|
||||
def __save_session_state(self):
|
||||
"""Saves the libtorrent session state"""
|
||||
try:
|
||||
open(deluge.configmanager.get_config_dir("session.state"), "wb").write(
|
||||
lt.bencode(self.session.state()))
|
||||
lt_data = open(deluge.configmanager.get_config_dir("session.state"), "wb")
|
||||
lt_data.write(lt.bencode(self.session.save_state()))
|
||||
lt_data.close()
|
||||
except Exception, e:
|
||||
log.warning("Failed to save lt state: %s", e)
|
||||
|
||||
@ -236,20 +248,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):
|
||||
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())
|
||||
return failure
|
||||
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 +774,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 +799,10 @@ class Core(component.Component):
|
||||
"""
|
||||
if not path:
|
||||
path = self.config["download_location"]
|
||||
try:
|
||||
return deluge.common.free_space(path)
|
||||
except InvalidPathError:
|
||||
return 0
|
||||
|
||||
@export
|
||||
def get_libtorrent_version(self):
|
||||
|
@ -110,7 +110,7 @@ class Daemon(object):
|
||||
|
||||
# Twisted catches signals to terminate, so just have it call the shutdown
|
||||
# method.
|
||||
reactor.addSystemEventTrigger("after", "shutdown", self.shutdown)
|
||||
reactor.addSystemEventTrigger("before", "shutdown", self._shutdown)
|
||||
|
||||
# Catch some Windows specific signals
|
||||
if deluge.common.windows_check():
|
||||
@ -120,7 +120,7 @@ class Daemon(object):
|
||||
def win_handler(ctrl_type):
|
||||
log.debug("ctrl_type: %s", ctrl_type)
|
||||
if ctrl_type == CTRL_CLOSE_EVENT or ctrl_type == CTRL_SHUTDOWN_EVENT:
|
||||
self.__shutdown()
|
||||
self._shutdown()
|
||||
return 1
|
||||
SetConsoleCtrlHandler(win_handler)
|
||||
|
||||
@ -133,9 +133,14 @@ class Daemon(object):
|
||||
if options and options.config:
|
||||
deluge.configmanager.set_config_dir(options.config)
|
||||
|
||||
if options and options.listen_interface:
|
||||
listen_interface = options.listen_interface
|
||||
else:
|
||||
listen_interface = ""
|
||||
|
||||
from deluge.core.core import Core
|
||||
# Start the core as a thread and join it until it's done
|
||||
self.core = Core()
|
||||
self.core = Core(listen_interface=listen_interface)
|
||||
|
||||
port = self.core.config["daemon_port"]
|
||||
if options and options.port:
|
||||
@ -177,17 +182,16 @@ class Daemon(object):
|
||||
reactor.callLater(0, reactor.stop)
|
||||
|
||||
def _shutdown(self, *args, **kwargs):
|
||||
if os.path.exists(deluge.configmanager.get_config_dir("deluged.pid")):
|
||||
try:
|
||||
os.remove(deluge.configmanager.get_config_dir("deluged.pid"))
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
log.error("Error removing deluged.pid!")
|
||||
|
||||
component.shutdown()
|
||||
try:
|
||||
reactor.stop()
|
||||
except twisted.internet.error.ReactorNotRunning:
|
||||
log.debug("Tried to stop the reactor but it is not running..")
|
||||
log.info("Waiting for components to shutdown..")
|
||||
d = component.shutdown()
|
||||
return d
|
||||
|
||||
@export()
|
||||
def info(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)
|
||||
try:
|
||||
handler(*event.args)
|
||||
except:
|
||||
log.error("Event handler %s failed in %s", event.name, handler)
|
||||
|
||||
def register_event_handler(self, event, handler):
|
||||
"""
|
||||
|
@ -91,7 +91,7 @@ def tracker_error_filter(torrent_ids, values):
|
||||
# Check all the torrent's tracker_status for 'Error:' and only return torrent_ids
|
||||
# that have this substring in their tracker_status
|
||||
for torrent_id in torrent_ids:
|
||||
if "Error:" in tm[torrent_id].get_status(["tracker_status"])["tracker_status"]:
|
||||
if _("Error") + ":" in tm[torrent_id].get_status(["tracker_status"])["tracker_status"]:
|
||||
filtered_torrent_ids.append(torrent_id)
|
||||
|
||||
return filtered_torrent_ids
|
||||
@ -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",
|
||||
@ -229,10 +228,15 @@ class PreferencesManager(component.Component):
|
||||
self.config.register_change_callback(self._on_config_value_change)
|
||||
|
||||
def stop(self):
|
||||
if self.new_release_timer:
|
||||
if self.new_release_timer and self.new_release_timer.running:
|
||||
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..")
|
||||
@ -467,14 +456,14 @@ class PreferencesManager(component.Component):
|
||||
if value:
|
||||
log.debug("Checking for new release..")
|
||||
threading.Thread(target=self.core.get_new_release).start()
|
||||
if self.new_release_timer:
|
||||
if self.new_release_timer and self.new_release_timer.running:
|
||||
self.new_release_timer.stop()
|
||||
# Set a timer to check for a new release every 3 days
|
||||
self.new_release_timer = LoopingCall(
|
||||
self._on_new_release_check, "new_release_check", True)
|
||||
self.new_release_timer.start(72 * 60 * 60, False)
|
||||
else:
|
||||
if self.new_release_timer:
|
||||
if self.new_release_timer and self.new_release_timer.running:
|
||||
self.new_release_timer.stop()
|
||||
|
||||
def _on_set_proxies(self, key, value):
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -424,6 +428,21 @@ class RPCServer(component.Component):
|
||||
"""
|
||||
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.
|
||||
|
@ -49,6 +49,31 @@ from deluge.event import *
|
||||
|
||||
TORRENT_STATE = deluge.common.TORRENT_STATE
|
||||
|
||||
def sanitize_filepath(filepath, folder=False):
|
||||
"""
|
||||
Returns a sanitized filepath to pass to libotorrent rename_file().
|
||||
The filepath will have backslashes substituted along with whitespace
|
||||
padding and duplicate slashes stripped. If `folder` is True a trailing
|
||||
slash is appended to the returned filepath.
|
||||
"""
|
||||
def clean_filename(filename):
|
||||
filename = filename.strip()
|
||||
if filename.replace('.', '') == '':
|
||||
return ''
|
||||
return filename
|
||||
|
||||
if '\\' in filepath or '/' in filepath:
|
||||
folderpath = filepath.replace('\\', '/').split('/')
|
||||
folderpath = [clean_filename(x) for x in folderpath]
|
||||
newfilepath = '/'.join(filter(None, folderpath))
|
||||
else:
|
||||
newfilepath = clean_filename(filepath)
|
||||
|
||||
if folder is True:
|
||||
return newfilepath + '/'
|
||||
else:
|
||||
return newfilepath
|
||||
|
||||
class TorrentOptions(dict):
|
||||
def __init__(self):
|
||||
config = ConfigManager("core.conf").config
|
||||
@ -179,6 +204,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 ##
|
||||
@ -286,7 +316,9 @@ class Torrent(object):
|
||||
self.update_state()
|
||||
break
|
||||
|
||||
self.options["file_priorities"] = file_priorities
|
||||
self.options["file_priorities"] = self.handle.file_priorities()
|
||||
if self.options["file_priorities"] != list(file_priorities):
|
||||
log.warning("File priorities were not set for this torrent")
|
||||
|
||||
# Set the first/last priorities if needed
|
||||
self.set_prioritize_first_last(self.options["prioritize_first_last_pieces"])
|
||||
@ -341,6 +373,9 @@ class Torrent(object):
|
||||
|
||||
# Set self.state to the ltstate right away just incase we don't hit some
|
||||
# of the logic below
|
||||
if ltstate in LTSTATE:
|
||||
self.state = LTSTATE[ltstate]
|
||||
else:
|
||||
self.state = str(ltstate)
|
||||
|
||||
log.debug("set_state_based_on_ltstate: %s", deluge.common.LT_TORRENT_STATE[ltstate])
|
||||
@ -393,15 +428,14 @@ 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
|
||||
left = status.total_wanted - status.total_wanted_done
|
||||
|
||||
if left <= 0 or status.download_payload_rate == 0:
|
||||
return 0
|
||||
@ -475,11 +509,11 @@ class Torrent(object):
|
||||
ret.append({
|
||||
"client": client,
|
||||
"country": country,
|
||||
"down_speed": peer.down_speed,
|
||||
"down_speed": peer.payload_down_speed,
|
||||
"ip": "%s:%s" % (peer.ip[0], peer.ip[1]),
|
||||
"progress": peer.progress,
|
||||
"seed": peer.flags & peer.seed,
|
||||
"up_speed": peer.up_speed,
|
||||
"up_speed": peer.payload_up_speed,
|
||||
})
|
||||
|
||||
return ret
|
||||
@ -568,6 +602,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 +627,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 +638,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"],
|
||||
@ -761,13 +805,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.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 +833,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,18 +913,22 @@ 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):
|
||||
"""Renames files in the torrent. 'filenames' should be a list of
|
||||
(index, filename) pairs."""
|
||||
for index, filename in filenames:
|
||||
filename = sanitize_filepath(filename)
|
||||
self.handle.rename_file(index, filename.encode("utf-8"))
|
||||
|
||||
def rename_folder(self, folder, new_folder):
|
||||
@ -879,8 +939,7 @@ 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 += "/"
|
||||
new_folder = sanitize_filepath(new_folder, folder=True)
|
||||
|
||||
wait_on_folder = (folder, new_folder, [])
|
||||
for f in self.get_files():
|
||||
|
@ -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
|
||||
|
||||
@ -151,6 +150,9 @@ class TorrentManager(component.Component):
|
||||
# Keeps track of resume data that needs to be saved to disk
|
||||
self.resume_data = {}
|
||||
|
||||
# Workaround to determine if TorrentAddedEvent is from state file
|
||||
self.session_started = False
|
||||
|
||||
# Register set functions
|
||||
self.config.register_set_function("max_connections_per_torrent",
|
||||
self.on_set_max_connections_per_torrent)
|
||||
@ -192,6 +194,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 +260,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():
|
||||
@ -376,7 +377,35 @@ class TorrentManager(component.Component):
|
||||
|
||||
add_torrent_params["resume_data"] = resume_data
|
||||
else:
|
||||
# We have a torrent_info object so we're not loading from state.
|
||||
# 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 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)
|
||||
add_torrent_trackers = []
|
||||
for value in torrent_info.trackers():
|
||||
tracker = {}
|
||||
tracker["url"] = value.url
|
||||
tracker["tier"] = value.tier
|
||||
add_torrent_trackers.append(tracker)
|
||||
|
||||
torrent_trackers = {}
|
||||
tracker_list = []
|
||||
for tracker in self[add_torrent_id].get_status(["trackers"])["trackers"]:
|
||||
torrent_trackers[(tracker["url"])] = tracker
|
||||
tracker_list.append(tracker)
|
||||
|
||||
added_tracker = False
|
||||
for tracker in add_torrent_trackers:
|
||||
if tracker['url'] not in torrent_trackers:
|
||||
tracker_list.append(tracker)
|
||||
added_tracker = True
|
||||
|
||||
if added_tracker:
|
||||
self[add_torrent_id].set_trackers(tracker_list)
|
||||
return
|
||||
|
||||
# Check if options is None and load defaults
|
||||
if options == None:
|
||||
options = TorrentOptions()
|
||||
@ -388,9 +417,10 @@ 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, name in options["mapped_files"].items():
|
||||
log.debug("renaming file index %s to %s", index, name)
|
||||
torrent_info.rename_file(index, utf8_encoded(name))
|
||||
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))
|
||||
|
||||
add_torrent_params["ti"] = torrent_info
|
||||
add_torrent_params["resume_data"] = ""
|
||||
@ -418,7 +448,7 @@ class TorrentManager(component.Component):
|
||||
handle = None
|
||||
try:
|
||||
if magnet:
|
||||
handle = lt.add_magnet_uri(self.session, magnet, add_torrent_params)
|
||||
handle = lt.add_magnet_uri(self.session, utf8_encoded(magnet), add_torrent_params)
|
||||
else:
|
||||
handle = self.session.add_torrent(add_torrent_params)
|
||||
except RuntimeError, e:
|
||||
@ -476,6 +506,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 +546,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 +595,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):
|
||||
@ -575,7 +608,7 @@ class TorrentManager(component.Component):
|
||||
os.path.join(get_config_dir(), "state", "torrents.state"), "rb")
|
||||
state = cPickle.load(state_file)
|
||||
state_file.close()
|
||||
except (EOFError, IOError, Exception), e:
|
||||
except (EOFError, IOError, Exception, cPickle.UnpicklingError), e:
|
||||
log.warning("Unable to load state file: %s", e)
|
||||
|
||||
# Try to use an old state
|
||||
@ -602,6 +635,7 @@ class TorrentManager(component.Component):
|
||||
log.error("Torrent state file is either corrupt or incompatible! %s", e)
|
||||
break
|
||||
|
||||
self.session_started = True
|
||||
component.get("EventManager").emit(SessionStartedEvent())
|
||||
|
||||
def save_state(self):
|
||||
@ -649,8 +683,8 @@ class TorrentManager(component.Component):
|
||||
state_file.flush()
|
||||
os.fsync(state_file.fileno())
|
||||
state_file.close()
|
||||
except IOError:
|
||||
log.warning("Unable to save state file.")
|
||||
except IOError, e:
|
||||
log.warning("Unable to save state file: %s", e)
|
||||
return True
|
||||
|
||||
# We have to move the 'torrents.state.new' file to 'torrents.state'
|
||||
@ -731,6 +765,39 @@ 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, (errno, strerror):
|
||||
from errno import ENOTEMPTY
|
||||
if errno == ENOTEMPTY:
|
||||
# Error raised if folder is not empty
|
||||
log.debug("%s", strerror)
|
||||
|
||||
except OSError, (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:
|
||||
@ -790,9 +857,9 @@ class TorrentManager(component.Component):
|
||||
log.debug("on_alert_torrent_finished")
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
except:
|
||||
return
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
log.debug("%s is finished..", torrent_id)
|
||||
|
||||
# Get the total_download and if it's 0, do not move.. It's likely
|
||||
@ -808,9 +875,9 @@ class TorrentManager(component.Component):
|
||||
if torrent.options["download_location"] != move_path:
|
||||
torrent.move_storage(move_path)
|
||||
|
||||
torrent.is_finished = True
|
||||
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
|
||||
|
||||
torrent.is_finished = True
|
||||
torrent.update_state()
|
||||
|
||||
# Only save resume data if it was actually downloaded something. Helps
|
||||
@ -825,9 +892,9 @@ class TorrentManager(component.Component):
|
||||
log.debug("on_alert_torrent_paused")
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
except:
|
||||
return
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
# Set the torrent state
|
||||
old_state = torrent.state
|
||||
torrent.update_state()
|
||||
@ -850,6 +917,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()
|
||||
|
||||
@ -905,17 +979,16 @@ class TorrentManager(component.Component):
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
torrent.set_save_path(alert.handle.save_path())
|
||||
torrent.set_save_path(os.path.normpath(alert.handle.save_path()))
|
||||
torrent.set_move_completed(False)
|
||||
|
||||
def on_alert_torrent_resumed(self, alert):
|
||||
log.debug("on_alert_torrent_resumed")
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
except:
|
||||
return
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
torrent.is_finished = torrent.handle.is_seed()
|
||||
old_state = torrent.state
|
||||
torrent.update_state()
|
||||
if torrent.state != old_state:
|
||||
@ -933,16 +1006,19 @@ class TorrentManager(component.Component):
|
||||
|
||||
old_state = torrent.state
|
||||
torrent.update_state()
|
||||
|
||||
# Torrent may need to download data after checking.
|
||||
if torrent.state in ('Checking', 'Checking Resume Data', 'Downloading'):
|
||||
torrent.is_finished = False
|
||||
|
||||
# Only emit a state changed event if the state has actually changed
|
||||
if torrent.state != old_state:
|
||||
component.get("EventManager").emit(TorrentStateChangedEvent(torrent_id, torrent.state))
|
||||
|
||||
def on_alert_save_resume_data(self, alert):
|
||||
log.debug("on_alert_save_resume_data")
|
||||
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
|
||||
try:
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
torrent = self.torrents[torrent_id]
|
||||
except:
|
||||
return
|
||||
@ -973,9 +1049,9 @@ class TorrentManager(component.Component):
|
||||
log.debug("index: %s name: %s", alert.index, alert.name.decode("utf8"))
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
except:
|
||||
return
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
|
||||
# We need to see if this file index is in a waiting_on_folder list
|
||||
folder_rename = False
|
||||
@ -985,6 +1061,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 +1090,12 @@ class TorrentManager(component.Component):
|
||||
except:
|
||||
return
|
||||
torrent.update_state()
|
||||
|
||||
def on_alert_file_completed(self, alert):
|
||||
log.debug("file_completed_alert: %s", alert.message())
|
||||
try:
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
except:
|
||||
return
|
||||
component.get("EventManager").emit(
|
||||
TorrentFileCompletedEvent(torrent_id, alert.index))
|
||||
|
@ -1,2 +0,0 @@
|
||||
From: http://famfamfam.com/lab/icons/flags/
|
||||
"These flag icons are available for free use for any purpose with no requirement for attribution."
|
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 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=Deluge BitTorrent Client
|
||||
GenericName=Bittorrent Client
|
||||
Comment=Transfer files using the Bittorrent protocol
|
||||
Exec=deluge-gtk
|
||||
Icon=deluge
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;
|
||||
StartupNotify=true
|
||||
MimeType=application/x-bittorrent;
|
14
deluge/data/share/applications/deluge.desktop.in
Normal file
14
deluge/data/share/applications/deluge.desktop.in
Normal file
@ -0,0 +1,14 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
_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;FileTransfer;P2P;GTK
|
||||
StartupNotify=true
|
||||
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.
|
||||
|
@ -51,6 +51,8 @@ class HTTPDownloader(client.HTTPDownloader):
|
||||
:type url: string
|
||||
:param filename: the filename to save the file as
|
||||
:type filename: string
|
||||
:param force_filename: forces use of the supplied filename, regardless of header content
|
||||
:type force_filename: bool
|
||||
:param part_callback: a function to be called when a part of data
|
||||
is received, it's signature should be: func(data, current_length, total_length)
|
||||
:type part_callback: function
|
||||
@ -84,13 +86,18 @@ class HTTPDownloader(client.HTTPDownloader):
|
||||
self.decoder = zlib.decompressobj(zlib.MAX_WBITS + 32)
|
||||
|
||||
if "content-disposition" in headers and not self.force_filename:
|
||||
try:
|
||||
new_file_name = str(headers["content-disposition"][0]).split(";")[1].split("=")[1]
|
||||
new_file_name = sanitise_filename(new_file_name)
|
||||
new_file_name = os.path.join(os.path.split(self.fileName)[0], new_file_name)
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
else:
|
||||
|
||||
count = 1
|
||||
fileroot = os.path.splitext(new_file_name)[0]
|
||||
fileext = os.path.splitext(new_file_name)[1]
|
||||
while os.path.isfile(new_file_name):
|
||||
# Increment filename if already exists
|
||||
new_file_name = "%s-%s%s" % (fileroot, count, fileext)
|
||||
count += 1
|
||||
|
||||
self.fileName = new_file_name
|
||||
self.value = new_file_name
|
||||
|
||||
@ -129,8 +136,6 @@ def sanitise_filename(filename):
|
||||
:type filename: string
|
||||
:returns: the sanitised filename
|
||||
:rtype: string
|
||||
|
||||
:raises IOError: when the filename exists
|
||||
"""
|
||||
|
||||
# Remove any quotes
|
||||
@ -147,9 +152,6 @@ def sanitise_filename(filename):
|
||||
# Dodgy server, log it
|
||||
log.warning("Potentially malicious server: trying to write to file '%s'" % filename)
|
||||
|
||||
if os.path.exists(filename):
|
||||
raise IOError, "File '%s' already exists!" % filename
|
||||
|
||||
return filename
|
||||
|
||||
def download_file(url, filename, callback=None, headers=None, force_filename=False, allow_compression=True):
|
||||
|
@ -1,290 +0,0 @@
|
||||
deluge/configmanager.py
|
||||
deluge/httpdownloader.py
|
||||
deluge/error.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/maketorrent.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/__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/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/webui/setup.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/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/__init__.py
|
||||
deluge/plugins/blocklist/blocklist/common.py
|
||||
deluge/plugins/blocklist/blocklist/core.py
|
||||
deluge/plugins/blocklist/blocklist/webui.py
|
||||
deluge/plugins/blocklist/blocklist/gtkui.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
|
4816
deluge/i18n/ar.po
4816
deluge/i18n/ar.po
File diff suppressed because it is too large
Load Diff
5978
deluge/i18n/ast.po
5978
deluge/i18n/ast.po
File diff suppressed because it is too large
Load Diff
6003
deluge/i18n/be.po
6003
deluge/i18n/be.po
File diff suppressed because it is too large
Load Diff
4266
deluge/i18n/bg.po
4266
deluge/i18n/bg.po
File diff suppressed because it is too large
Load Diff
6274
deluge/i18n/bn.po
6274
deluge/i18n/bn.po
File diff suppressed because it is too large
Load Diff
6284
deluge/i18n/bs.po
6284
deluge/i18n/bs.po
File diff suppressed because it is too large
Load Diff
4986
deluge/i18n/ca.po
4986
deluge/i18n/ca.po
File diff suppressed because it is too large
Load Diff
4275
deluge/i18n/cs.po
4275
deluge/i18n/cs.po
File diff suppressed because it is too large
Load Diff
6293
deluge/i18n/cy.po
6293
deluge/i18n/cy.po
File diff suppressed because it is too large
Load Diff
4523
deluge/i18n/da.po
4523
deluge/i18n/da.po
File diff suppressed because it is too large
Load Diff
4334
deluge/i18n/de.po
4334
deluge/i18n/de.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
5065
deluge/i18n/el.po
5065
deluge/i18n/el.po
File diff suppressed because it is too large
Load Diff
4312
deluge/i18n/en_AU.po
4312
deluge/i18n/en_AU.po
File diff suppressed because it is too large
Load Diff
4580
deluge/i18n/en_CA.po
4580
deluge/i18n/en_CA.po
File diff suppressed because it is too large
Load Diff
4846
deluge/i18n/en_GB.po
4846
deluge/i18n/en_GB.po
File diff suppressed because it is too large
Load Diff
6293
deluge/i18n/eo.po
6293
deluge/i18n/eo.po
File diff suppressed because it is too large
Load Diff
4363
deluge/i18n/es.po
4363
deluge/i18n/es.po
File diff suppressed because it is too large
Load Diff
4823
deluge/i18n/et.po
4823
deluge/i18n/et.po
File diff suppressed because it is too large
Load Diff
5100
deluge/i18n/eu.po
5100
deluge/i18n/eu.po
File diff suppressed because it is too large
Load Diff
5662
deluge/i18n/fa.po
5662
deluge/i18n/fa.po
File diff suppressed because it is too large
Load Diff
4405
deluge/i18n/fi.po
4405
deluge/i18n/fi.po
File diff suppressed because it is too large
Load Diff
3743
deluge/i18n/fo.po
Normal file
3743
deluge/i18n/fo.po
Normal file
File diff suppressed because it is too large
Load Diff
4538
deluge/i18n/fr.po
4538
deluge/i18n/fr.po
File diff suppressed because it is too large
Load Diff
7200
deluge/i18n/fy.po
7200
deluge/i18n/fy.po
File diff suppressed because it is too large
Load Diff
4347
deluge/i18n/gl.po
4347
deluge/i18n/gl.po
File diff suppressed because it is too large
Load Diff
4232
deluge/i18n/he.po
4232
deluge/i18n/he.po
File diff suppressed because it is too large
Load Diff
4346
deluge/i18n/hi.po
4346
deluge/i18n/hi.po
File diff suppressed because it is too large
Load Diff
5770
deluge/i18n/hr.po
5770
deluge/i18n/hr.po
File diff suppressed because it is too large
Load Diff
3992
deluge/i18n/hu.po
3992
deluge/i18n/hu.po
File diff suppressed because it is too large
Load Diff
4116
deluge/i18n/id.po
4116
deluge/i18n/id.po
File diff suppressed because it is too large
Load Diff
4216
deluge/i18n/is.po
4216
deluge/i18n/is.po
File diff suppressed because it is too large
Load Diff
4998
deluge/i18n/it.po
4998
deluge/i18n/it.po
File diff suppressed because it is too large
Load Diff
6244
deluge/i18n/iu.po
6244
deluge/i18n/iu.po
File diff suppressed because it is too large
Load Diff
4183
deluge/i18n/ja.po
4183
deluge/i18n/ja.po
File diff suppressed because it is too large
Load Diff
5626
deluge/i18n/ka.po
5626
deluge/i18n/ka.po
File diff suppressed because it is too large
Load Diff
4410
deluge/i18n/kk.po
4410
deluge/i18n/kk.po
File diff suppressed because it is too large
Load Diff
6344
deluge/i18n/kn.po
6344
deluge/i18n/kn.po
File diff suppressed because it is too large
Load Diff
4229
deluge/i18n/ko.po
4229
deluge/i18n/ko.po
File diff suppressed because it is too large
Load Diff
6258
deluge/i18n/ku.po
6258
deluge/i18n/ku.po
File diff suppressed because it is too large
Load Diff
6251
deluge/i18n/la.po
6251
deluge/i18n/la.po
File diff suppressed because it is too large
Load Diff
3743
deluge/i18n/lb.po
Normal file
3743
deluge/i18n/lb.po
Normal file
File diff suppressed because it is too large
Load Diff
4756
deluge/i18n/lt.po
4756
deluge/i18n/lt.po
File diff suppressed because it is too large
Load Diff
4801
deluge/i18n/lv.po
4801
deluge/i18n/lv.po
File diff suppressed because it is too large
Load Diff
5895
deluge/i18n/mk.po
5895
deluge/i18n/mk.po
File diff suppressed because it is too large
Load Diff
3743
deluge/i18n/ml.po
Normal file
3743
deluge/i18n/ml.po
Normal file
File diff suppressed because it is too large
Load Diff
4816
deluge/i18n/ms.po
4816
deluge/i18n/ms.po
File diff suppressed because it is too large
Load Diff
4638
deluge/i18n/nb.po
4638
deluge/i18n/nb.po
File diff suppressed because it is too large
Load Diff
6253
deluge/i18n/nds.po
6253
deluge/i18n/nds.po
File diff suppressed because it is too large
Load Diff
4822
deluge/i18n/nl.po
4822
deluge/i18n/nl.po
File diff suppressed because it is too large
Load Diff
3746
deluge/i18n/nn.po
Normal file
3746
deluge/i18n/nn.po
Normal file
File diff suppressed because it is too large
Load Diff
3759
deluge/i18n/oc.po
Normal file
3759
deluge/i18n/oc.po
Normal file
File diff suppressed because it is too large
Load Diff
4776
deluge/i18n/pl.po
4776
deluge/i18n/pl.po
File diff suppressed because it is too large
Load Diff
6245
deluge/i18n/pms.po
6245
deluge/i18n/pms.po
File diff suppressed because it is too large
Load Diff
5025
deluge/i18n/pt.po
5025
deluge/i18n/pt.po
File diff suppressed because it is too large
Load Diff
4929
deluge/i18n/pt_BR.po
4929
deluge/i18n/pt_BR.po
File diff suppressed because it is too large
Load Diff
4996
deluge/i18n/ro.po
4996
deluge/i18n/ro.po
File diff suppressed because it is too large
Load Diff
3882
deluge/i18n/ru.po
3882
deluge/i18n/ru.po
File diff suppressed because it is too large
Load Diff
6244
deluge/i18n/si.po
6244
deluge/i18n/si.po
File diff suppressed because it is too large
Load Diff
4992
deluge/i18n/sk.po
4992
deluge/i18n/sk.po
File diff suppressed because it is too large
Load Diff
4934
deluge/i18n/sl.po
4934
deluge/i18n/sl.po
File diff suppressed because it is too large
Load Diff
4434
deluge/i18n/sr.po
4434
deluge/i18n/sr.po
File diff suppressed because it is too large
Load Diff
4941
deluge/i18n/sv.po
4941
deluge/i18n/sv.po
File diff suppressed because it is too large
Load Diff
6251
deluge/i18n/ta.po
6251
deluge/i18n/ta.po
File diff suppressed because it is too large
Load Diff
3743
deluge/i18n/te.po
Normal file
3743
deluge/i18n/te.po
Normal file
File diff suppressed because it is too large
Load Diff
4083
deluge/i18n/th.po
4083
deluge/i18n/th.po
File diff suppressed because it is too large
Load Diff
6245
deluge/i18n/tl.po
6245
deluge/i18n/tl.po
File diff suppressed because it is too large
Load Diff
6244
deluge/i18n/tlh.po
6244
deluge/i18n/tlh.po
File diff suppressed because it is too large
Load Diff
4382
deluge/i18n/tr.po
4382
deluge/i18n/tr.po
File diff suppressed because it is too large
Load Diff
4810
deluge/i18n/uk.po
4810
deluge/i18n/uk.po
File diff suppressed because it is too large
Load Diff
3743
deluge/i18n/ur.po
Normal file
3743
deluge/i18n/ur.po
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user