Compare commits
279 Commits
release-1.
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
429ff34e46 | ||
|
|
54bf7e61f7 | ||
|
|
8422cb395a | ||
|
|
313b9b9bc0 | ||
|
|
9d3fcb10d5 | ||
|
|
34725dc26b | ||
|
|
8b576fcc50 | ||
|
|
1b7449ef4e | ||
|
|
596ec6ae4c | ||
|
|
569b69689a | ||
|
|
07c195f475 | ||
|
|
5e86c8a14d | ||
|
|
85796d9c2c | ||
|
|
f4120088c6 | ||
|
|
8d1f1512b0 | ||
|
|
c6abdacadd | ||
|
|
fa00d745b2 | ||
|
|
1e1313dffc | ||
|
|
00b3677867 | ||
|
|
8a9a23866f | ||
|
|
91328de30f | ||
|
|
204d9ff496 | ||
|
|
5d64373798 | ||
|
|
699d4b1161 | ||
|
|
0fe9d68be1 | ||
|
|
b2f9394d6e | ||
|
|
56965d444a | ||
|
|
07d7a44731 | ||
|
|
f85aa72c72 | ||
|
|
2f5b4a190e | ||
|
|
aada4f3c03 | ||
|
|
9540d45c1d | ||
|
|
cfd7a1bf0b | ||
|
|
3e13bd1381 | ||
|
|
3af23ae8a9 | ||
|
|
f1aa1f993d | ||
|
|
82b1b29906 | ||
|
|
14882fe38f | ||
|
|
82f60f1b89 | ||
|
|
dab617458a | ||
|
|
1832d518d6 | ||
|
|
5ed8517cde | ||
|
|
b2950afd5c | ||
|
|
a84c686090 | ||
|
|
efb8315a38 | ||
|
|
56b018fb95 | ||
|
|
731438a19a | ||
|
|
fd4ce53eb1 | ||
|
|
b4f0fbdbc4 | ||
|
|
26ae2af198 | ||
|
|
bdb625a615 | ||
|
|
f68c5114b2 | ||
|
|
83282bf835 | ||
|
|
054351e3a4 | ||
|
|
a5dac03956 | ||
|
|
a760913482 | ||
|
|
f3c8889865 | ||
|
|
b24a1e9123 | ||
|
|
c4b7622a17 | ||
|
|
f7fa66c65d | ||
|
|
a91d2028d8 | ||
|
|
adea644c04 | ||
|
|
31679f0d6d | ||
|
|
0d90ad8f7c | ||
|
|
8ab6aeafcc | ||
|
|
a56baf5b58 | ||
|
|
61eb921e44 | ||
|
|
8ecf79e91c | ||
|
|
349e58fa22 | ||
|
|
436f5bbd7e | ||
|
|
64bfdf930c | ||
|
|
8858518a14 | ||
|
|
def19ef88b | ||
|
|
4f989b674f | ||
|
|
f0ef21e409 | ||
|
|
6b0ae4f575 | ||
|
|
3926b0d787 | ||
|
|
a721e6d943 | ||
|
|
9c05148a24 | ||
|
|
725ef52cfe | ||
|
|
7982011d0b | ||
|
|
0a3bb0cfcd | ||
|
|
f75501f781 | ||
|
|
70f8a00c20 | ||
|
|
2691677650 | ||
|
|
be34bed61a | ||
|
|
721ef1d236 | ||
|
|
c8b944508b | ||
|
|
f3f3f2822b | ||
|
|
3ef0c82a8c | ||
|
|
b8200fd7b2 | ||
|
|
9e62780a6d | ||
|
|
58c78fa6f6 | ||
|
|
5484ec923d | ||
|
|
54225cd42f | ||
|
|
dc651ffed3 | ||
|
|
476a7d7be0 | ||
|
|
c580285fe8 | ||
|
|
5b6166ee15 | ||
|
|
18ee43225e | ||
|
|
b47073901f | ||
|
|
d45c44868e | ||
|
|
4401b60d51 | ||
|
|
0ce114a7f8 | ||
|
|
38ca306499 | ||
|
|
6cb636f85d | ||
|
|
7563b09ead | ||
|
|
76783c819c | ||
|
|
42312b982b | ||
|
|
a329a59719 | ||
|
|
82e548a2fd | ||
|
|
5bbdb9e119 | ||
|
|
68cbcaeab9 | ||
|
|
3c88657e0f | ||
|
|
f836be6736 | ||
|
|
85fe900afe | ||
|
|
052ccf2302 | ||
|
|
ae384da609 | ||
|
|
0fc2d289eb | ||
|
|
9ca02aad88 | ||
|
|
8ea34135e4 | ||
|
|
b66be5b64b | ||
|
|
ebc738e3a5 | ||
|
|
713474bc43 | ||
|
|
265a8cada8 | ||
|
|
138af31ef9 | ||
|
|
a5f6663e65 | ||
|
|
1519bca46d | ||
|
|
03719cbb87 | ||
|
|
98825613fa | ||
|
|
47abadfc67 | ||
|
|
a220e01e2e | ||
|
|
dea9237aa0 | ||
|
|
6099544da5 | ||
|
|
902116cbc8 | ||
|
|
96a6f450d0 | ||
|
|
6ed565a06b | ||
|
|
1fbbee794c | ||
|
|
bfab6389b5 | ||
|
|
26a4040003 | ||
|
|
e7ac721b3c | ||
|
|
48f5436f6f | ||
|
|
f754e34e35 | ||
|
|
9bb18e1a07 | ||
|
|
0245bbf5ab | ||
|
|
0a0c1f3529 | ||
|
|
be94c86350 | ||
|
|
a26333bc65 | ||
|
|
4c862597ae | ||
|
|
937d45d850 | ||
|
|
20b6579392 | ||
|
|
4f25e6c769 | ||
|
|
ce3b8733d1 | ||
|
|
b0140fbdbe | ||
|
|
abcd2b7600 | ||
|
|
f9998957cd | ||
|
|
acb8d5679f | ||
|
|
c3d3156ec7 | ||
|
|
09abd303bd | ||
|
|
0678e2e2bd | ||
|
|
00dbf7ba38 | ||
|
|
42cacabccd | ||
|
|
3d04a944b6 | ||
|
|
b02e2c2f9b | ||
|
|
5c0f17bf31 | ||
|
|
ac9a81985f | ||
|
|
a46c63d883 | ||
|
|
09a696fc27 | ||
|
|
dedc54d26f | ||
|
|
c0ffb8fa5c | ||
|
|
3e350c5935 | ||
|
|
da244d7ef2 | ||
|
|
2fc7039cc7 | ||
|
|
654497cd4a | ||
|
|
bdf50483df | ||
|
|
8a0681744e | ||
|
|
2699c76afd | ||
|
|
773064a081 | ||
|
|
7718403419 | ||
|
|
26b4d31810 | ||
|
|
f8dfda0893 | ||
|
|
e8897ae159 | ||
|
|
2f71f9f51a | ||
|
|
fc595f49d8 | ||
|
|
92444fccf4 | ||
|
|
6d0aebe9cb | ||
|
|
ef7af6bad6 | ||
|
|
c1a8a58080 | ||
|
|
76047f4ef2 | ||
|
|
9062266a84 | ||
|
|
9cdcd53234 | ||
|
|
419b94f042 | ||
|
|
3cb34ed7ee | ||
|
|
925ecb3464 | ||
|
|
488bd90303 | ||
|
|
c478ba59ac | ||
|
|
fe7c0db425 | ||
|
|
b2bf7047d9 | ||
|
|
e943449b42 | ||
|
|
69e2355ff4 | ||
|
|
b7ea2fb51a | ||
|
|
4c880fea09 | ||
|
|
a0e895641c | ||
|
|
484f1c36c9 | ||
|
|
982ce5c0ad | ||
|
|
7ac5da0acc | ||
|
|
9733716136 | ||
|
|
eebdc26e5a | ||
|
|
1f8b9378a3 | ||
|
|
b99b838827 | ||
|
|
c4d143c2c0 | ||
|
|
db1bd53f44 | ||
|
|
07dc182053 | ||
|
|
563055e891 | ||
|
|
b3de2ccc72 | ||
|
|
f4d5ceb3c4 | ||
|
|
70beef9f94 | ||
|
|
47c1908519 | ||
|
|
e1ead940be | ||
|
|
862d24ac48 | ||
|
|
078c80c81d | ||
|
|
6ba4588e62 | ||
|
|
cd70843ee9 | ||
|
|
cb8ecb74bf | ||
|
|
9f36d521a4 | ||
|
|
2c2c1093c3 | ||
|
|
0e81f3731f | ||
|
|
f1114387ae | ||
|
|
54516ac231 | ||
|
|
913f93ba82 | ||
|
|
11c7255da6 | ||
|
|
69fa916e26 | ||
|
|
d86335a58e | ||
|
|
24dced2411 | ||
|
|
1b71a4a4a4 | ||
|
|
ffedffcead | ||
|
|
1f64e14195 | ||
|
|
d6a5b1d321 | ||
|
|
792101731f | ||
|
|
3e783873ec | ||
|
|
ffb262f0a1 | ||
|
|
4727c1bdca | ||
|
|
1f1e37ce76 | ||
|
|
f40337d2e3 | ||
|
|
ecc5106f07 | ||
|
|
1b93f4eaf1 | ||
|
|
685ceafc5b | ||
|
|
cf6128a222 | ||
|
|
f78c74c1b5 | ||
|
|
faeee49517 | ||
|
|
1918969e28 | ||
|
|
fa9e3bf564 | ||
|
|
8eac154c05 | ||
|
|
ff12c76a62 | ||
|
|
2c27f952e2 | ||
|
|
e3214a9b6a | ||
|
|
4a02d171b4 | ||
|
|
7a16a1d8f1 | ||
|
|
ecc41fbc60 | ||
|
|
0125a2b521 | ||
|
|
d328b93c1a | ||
|
|
ab588b741e | ||
|
|
1588cd5d3d | ||
|
|
a597f067e2 | ||
|
|
0778f2a19e | ||
|
|
6c0d4b9439 | ||
|
|
98d91f7697 | ||
|
|
f7ac4ec652 | ||
|
|
43b96ea4f2 | ||
|
|
91bd5f1d8e | ||
|
|
f0edd7dcf7 | ||
|
|
e3f2480fe5 | ||
|
|
26e5785754 | ||
|
|
db12fb292a | ||
|
|
798ee52de1 | ||
|
|
b1f562d7c0 | ||
|
|
41daeb4c19 | ||
|
|
d66b788e5f | ||
|
|
ff4ab915a2 |
3
AUTHORS
@@ -1,5 +1,6 @@
|
|||||||
Author:
|
Author:
|
||||||
* Christophe Dumez <chris@qbittorrent.org>
|
* Christophe Dumez <chris@qbittorrent.org>
|
||||||
|
|
||||||
Other developers:
|
Contributors:
|
||||||
* Arnaud Demaizière <arnaud@qbittorrent.org>
|
* Arnaud Demaizière <arnaud@qbittorrent.org>
|
||||||
|
* Ishan Arora <ishanarora@gmail.com>
|
||||||
|
|||||||
45
Changelog
@@ -1,10 +1,37 @@
|
|||||||
* Unknown - Christophe Dumez <chris@qbittorrent.org> - v1.0.0
|
* Unknown - Christophe Dumez <chris@qbittorrent.org> - v1.1.0
|
||||||
|
- FEATURE: Web interface to control qbittorrent (Ishan Arora)
|
||||||
|
- FEATURE: Can spoof Azureus peer id to avoid ban
|
||||||
|
- FEATURE: Allow to hide/show some columns in download and seeding lists
|
||||||
|
- FEATURE: Option to start qBittorrent minimized in systray
|
||||||
|
- FEATURE: Allow to define double-click actions in torrents lists
|
||||||
|
- FEATURE: Allow to open torrent destination folder
|
||||||
|
- FEATURE: Real progress bar in torrent properties that displays downloaded pieces
|
||||||
|
- FEATURE: Allow to buy downloads using ShareMonkey
|
||||||
|
- FEATURE: Display if UPnP/NAT-PMP was successful or not
|
||||||
|
- FEATURE: Threadified torrent creation
|
||||||
|
- FEATURE: Improved eMule DAT ip filter parser
|
||||||
|
- FEATURE: Added support for PeerGuardian p2p filters (text)
|
||||||
|
- FEATURE: Added support for PeerGuardian p2b filters (binary)
|
||||||
|
- FEATURE: Allow to customize folder scan interval
|
||||||
|
- FEATURE: Allow to add several trackers at once
|
||||||
|
- BUGFIX: Do not display seeds number in seeding list (always 0)
|
||||||
|
- BUGFIX: Threadified IP filter file parser to avoid GUI freeze
|
||||||
|
- COSMETIC: Do not display progress bar in seeding list (always 100%)
|
||||||
|
- COSMETIC: Added a progress bar for torrent creation
|
||||||
|
- COSMETIC: Display tracker errors in a cleaner way
|
||||||
|
- COSMETIC: Display "unpaused/total_torrent" in download/upload tabs
|
||||||
|
- COSMETIC: Allow to resize RSS column
|
||||||
|
- COSMETIC: Global UP/DL speeds and ratio are displayed above tabs
|
||||||
|
|
||||||
|
* Fri Apr 11 2008 - Christophe Dumez <chris@qbittorrent.org> - v1.0.0
|
||||||
- FEATURE: Based on new libtorrent v0.13
|
- FEATURE: Based on new libtorrent v0.13
|
||||||
- FEATURE: Added UPnP / NAT-PMP port forwarding support
|
- FEATURE: Added UPnP / NAT-PMP port forwarding support
|
||||||
- FEATURE: Added encryption support (compatible with Azureus)
|
- FEATURE: Added encryption support (compatible with Azureus)
|
||||||
- FEATURE: Bittorrent FAST extension support
|
- FEATURE: Bittorrent FAST extension support
|
||||||
- FEATURE: Added RSS support
|
- FEATURE: Added RSS support
|
||||||
- FEATURE: Support files prioritizing in a torrent
|
- FEATURE: Support files prioritizing in a torrent
|
||||||
|
- FEATURE: Brand new search engine plugins system
|
||||||
|
- FEATURE: Filtered files don't appear on hard disk anymore
|
||||||
- FEATURE: Finished torrents are now moved to another tab for seeding
|
- FEATURE: Finished torrents are now moved to another tab for seeding
|
||||||
- FEATURE: Display more infos about the torrent in its properties
|
- FEATURE: Display more infos about the torrent in its properties
|
||||||
- FEATURE: Allow the user to edit torrents' trackers
|
- FEATURE: Allow the user to edit torrents' trackers
|
||||||
@@ -30,6 +57,15 @@
|
|||||||
- FEATURE: User is now warned when fast resume data was rejected
|
- FEATURE: User is now warned when fast resume data was rejected
|
||||||
- FEATURE: Url seeds are now displayed in torrent properties and are editable
|
- FEATURE: Url seeds are now displayed in torrent properties and are editable
|
||||||
- FEATURE: Allow to drag 'n drop urls on the main window
|
- FEATURE: Allow to drag 'n drop urls on the main window
|
||||||
|
- FEATURE: Improved search engine (multipage support in all plugins)
|
||||||
|
- FEATURE: Added BTJunkie search engine plugin
|
||||||
|
- FEATURE: Added an option to force full disk allocation for all torrents
|
||||||
|
- FEATURE: Added an option to add torrents in paused state
|
||||||
|
- FEATURE: Added an option to set the max number of connections per torrent
|
||||||
|
- FEATURE: Added an option to set the max number of uploads per torrent
|
||||||
|
- FEATURE: Added an option to automatically delete torrents when they reach a given ratio (>= 1.0)
|
||||||
|
- FEATURE: Added an option to display current transfer speeds in title bar
|
||||||
|
- FEATURE: Torrent content is now displayed as a tree
|
||||||
- I18N: Added Hungarian translation
|
- I18N: Added Hungarian translation
|
||||||
- I18N: Added Brazilian translation
|
- I18N: Added Brazilian translation
|
||||||
- BUGFIX: Progress of paused torrents is now correct on restart
|
- BUGFIX: Progress of paused torrents is now correct on restart
|
||||||
@@ -52,11 +88,16 @@
|
|||||||
- BUGFIX: Made torrent deletion from hard-drive safer
|
- BUGFIX: Made torrent deletion from hard-drive safer
|
||||||
- BUGFIX: Prevent downloadFromUrl flooding
|
- BUGFIX: Prevent downloadFromUrl flooding
|
||||||
- BUGFIX: ETA was wrong for torrents with filtered files
|
- BUGFIX: ETA was wrong for torrents with filtered files
|
||||||
|
- BUGFIX: Fixed drag'n drop on non-KDE systems
|
||||||
|
- BUGFIX: Removed build dependency on Python
|
||||||
|
- BUGFIX: Catching DHT exception in case there is a problem
|
||||||
- COSMETIC: Redesigned torrent properties a little
|
- COSMETIC: Redesigned torrent properties a little
|
||||||
- COSMETIC: Redesigned options a little
|
- COSMETIC: Totally redesigned program preferences
|
||||||
- COSMETIC: Display more logs messages concerning features
|
- COSMETIC: Display more logs messages concerning features
|
||||||
- COSMETIC: Improved lists renderers
|
- COSMETIC: Improved lists renderers
|
||||||
- COSMETIC: Use a different icon for torrents being checked and for connecting ones
|
- COSMETIC: Use a different icon for torrents being checked and for connecting ones
|
||||||
|
- COSMETIC: Improved some icons
|
||||||
|
- COSMETIC: Improved systray tooltip style
|
||||||
|
|
||||||
* Mon May 07 2007 - Christophe Dumez <chris@qbittorrent.org> - v0.9.3
|
* Mon May 07 2007 - Christophe Dumez <chris@qbittorrent.org> - v0.9.3
|
||||||
- BUGFIX: Fixed pause toggle on double-click in download list
|
- BUGFIX: Fixed pause toggle on double-click in download list
|
||||||
|
|||||||
23
INSTALL
@@ -1,4 +1,4 @@
|
|||||||
qBittorrent - A BitTorrent client in C++ / Qt4.2
|
qBittorrent - A BitTorrent client in C++ / Qt4
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
*** Necessary if qt3 is default on your system ***
|
*** Necessary if qt3 is default on your system ***
|
||||||
@@ -14,25 +14,30 @@ qbittorrent
|
|||||||
will install and execute qBittorrent hopefully without any problems.
|
will install and execute qBittorrent hopefully without any problems.
|
||||||
|
|
||||||
Dependencies:
|
Dependencies:
|
||||||
- Qt >= 4.2 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
|
- Qt >= 4.3.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
|
||||||
|
|
||||||
- libtorrent by Arvid Norberg (>= v0.13 REQUIRED)
|
- rblibtorrent by Arvid Norberg (>= v0.13 REQUIRED)
|
||||||
|
-> http://www.qbittorrent.org/download.php (advised)
|
||||||
-> http://www.libtorrent.net
|
-> http://www.libtorrent.net
|
||||||
Be carefull: another library (the one used by rtorrent) use the same name.
|
Be careful: another library (the one used by rTorrent) use the same name.
|
||||||
These are TWO different libraries and qBittorrent will only work with the one provided
|
These are TWO different libraries and qBittorrent will only work with the one provided
|
||||||
on sourceforge (created by Arvid Norberg). The two libraries conflicts with each other.
|
on sourceforge (created by Arvid Norberg). The two libraries conflicts with each other.
|
||||||
|
|
||||||
- libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization
|
- libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization
|
||||||
|
|
||||||
- libcommoncpp2
|
- libcurl
|
||||||
|
|
||||||
- python >= 2.3 (previous might work - not tested): needed by search engine.
|
- python >= 2.3 (needed by search engine)
|
||||||
|
|
||||||
- libmagick++ (advised, not required)
|
- libmagick++ (advised, not required)
|
||||||
|
* Needed for favicons support (RSS / Search plugins)
|
||||||
|
|
||||||
NOTE FOR GNOME USERS:
|
- libzzip (advised, not required)
|
||||||
- qt4-qtconfig package is advised when using Plastique style (default)
|
* Needed for zip support (Search plugins)
|
||||||
or just change qBittorrent style to Cleanlooks (GNOME like)
|
|
||||||
|
NOTE FOR NON-KDE USERS:
|
||||||
|
- qt4-qtconfig package is advised when using other systems than KDE.
|
||||||
|
You can also change qBittorrent style to Cleanlooks (GNOME like)
|
||||||
|
|
||||||
DOCUMENTATION:
|
DOCUMENTATION:
|
||||||
Please note that there is a documentation with a "compiling howto" at http://wiki.qbittorrent.org.
|
Please note that there is a documentation with a "compiling howto" at http://wiki.qbittorrent.org.
|
||||||
|
|||||||
14
README
@@ -3,30 +3,30 @@ qBittorrent - A BitTorrent client in Qt4
|
|||||||
|
|
||||||
Description:
|
Description:
|
||||||
********************************
|
********************************
|
||||||
qBittorrent is a bittorrent client programmed in C++ / Qt4 that use
|
qBittorrent is a bittorrent client programmed in C++ / Qt4 that uses
|
||||||
libtorrent (sometimes called rb_libtorrent) by Arvid Norberg.
|
libtorrent (sometimes called rblibtorrent) by Arvid Norberg.
|
||||||
|
|
||||||
It aims to be a good alternative to all other bittorrent clients
|
It aims to be a good alternative to all other bittorrent clients
|
||||||
out there. qBittorrent is fast, stable and provides unicode
|
out there. qBittorrent is fast, stable and provides unicode
|
||||||
support.
|
support as well as many features.
|
||||||
|
|
||||||
Installation:
|
Installation:
|
||||||
********************************
|
********************************
|
||||||
For installation follow the instructions from INSTALL file, but simple
|
For installation, follow the instructions from INSTALL file, but simple:
|
||||||
|
|
||||||
./configure
|
./configure
|
||||||
make && make install
|
make && make install
|
||||||
qbittorrent
|
qbittorrent
|
||||||
|
|
||||||
will install and execute qBittorrent hopefully without any problems.
|
will install and execute qBittorrent hopefully without any problem.
|
||||||
|
|
||||||
For more information please visit:
|
For more information please visit:
|
||||||
http://www.qbittorrent.org
|
http://www.qbittorrent.org
|
||||||
|
|
||||||
Please report any bug (or feature requests) to:
|
Please report any bug (or feature request) to:
|
||||||
http://bugs.qbittorrent.org
|
http://bugs.qbittorrent.org
|
||||||
|
|
||||||
You can also meet me on IRC:
|
You can also meet me (chris-qBT) on IRC:
|
||||||
#qbittorrent on irc.freenode.net
|
#qbittorrent on irc.freenode.net
|
||||||
|
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|||||||
95
TODO
@@ -1,98 +1,37 @@
|
|||||||
// Easy
|
// Easy
|
||||||
- Translations into as many languages as possible
|
- Translations into as many languages as possible
|
||||||
- Improve man page
|
|
||||||
- Use Launchpad/Rosetta for translations once it supports TS files
|
- Use Launchpad/Rosetta for translations once it supports TS files
|
||||||
|
|
||||||
// Intermediate
|
// Intermediate
|
||||||
- Port on MacOS, Windows (and create an installer for Windows) - Progressing slowly
|
- Port on MacOS, Windows (and create an installer for Windows) - Slow progress
|
||||||
- Add some transparency (menus,...), improve look
|
- Add some transparency (menus,...), improve look / usabilty
|
||||||
|
- Skins support? (contact Mateusz)
|
||||||
|
|
||||||
// Harder
|
// Harder
|
||||||
- Display a progress bar that really displays the pieces we have (like in eMule)
|
- Torrent scheduler ala µtorrent/Bitcomet
|
||||||
|
|
||||||
// Waiting for libtorrent
|
// Waiting for libtorrent
|
||||||
- File selection in a torrent in compact mode
|
- Allow to prioritize torrents (may code this in qBittorrent?)
|
||||||
- Allow to prioritize torrents
|
|
||||||
|
|
||||||
// Unsure
|
// Unsure
|
||||||
|
- Display the peers we are connected to for each torrent with infos (like flag, dl/up speeds, ...)
|
||||||
- Azureus spoofing to prevent ban from trackers?
|
- Azureus spoofing to prevent ban from trackers?
|
||||||
- Option to shutdown computer when downloads are finished
|
- Option to shutdown computer when downloads are finished
|
||||||
- Add a torrent scheduler
|
- NAT checker/Tester
|
||||||
|
- Display hard drive space left?
|
||||||
- Make use of dbus on Linux for the single instance instead of socket communication?
|
- Make use of dbus on Linux for the single instance instead of socket communication?
|
||||||
(http://techbase.kde.org/Development/Tutorials/D-Bus/Accessing_Interfaces)
|
(http://techbase.kde.org/Development/Tutorials/D-Bus/Accessing_Interfaces)
|
||||||
- search engines customizing
|
- When favicon can't be downloaded, try to parse the webpage for:
|
||||||
|
<link rel="icon" href="http://example.com/favicon.ico" type="image/vnd.microsoft.icon">
|
||||||
|
* Be careful, the link can be relative
|
||||||
|
- Improve search plugin install (choose in a list taken from plugins.qbittorrent.org)
|
||||||
|
- support zipped torrents? (useful?)
|
||||||
|
- Allow to limit the number of downloading torrents simultaneously (other are paused until a download finishes)
|
||||||
|
|
||||||
// in v1.2.0
|
// in v1.2.0
|
||||||
- Allow user to organize the downloads into categories/folders
|
- Allow user to organize the downloads into categories/folders?
|
||||||
|
|
||||||
// in v1.1.0
|
// in v1.1.0
|
||||||
- Tabs support in search
|
- Stop calculating ETAs when ETA column is hidden
|
||||||
- Allow to hide columns?
|
-> See https://blueprints.launchpad.net/qbittorrent
|
||||||
- Allow to scan multiple directories? (useful?)
|
|
||||||
- Web interface (turbogears? php?)
|
|
||||||
- improve and test tracker authentication code (remember login/pass) (need a tracker to test this)
|
|
||||||
- support zipped torrents? (useful?)
|
|
||||||
- Add option for RSS customization (refresh interval, max news per RSS...)
|
|
||||||
- Allow to disable UPnP/NAT-PMP/LSD in options?
|
|
||||||
- Allow to automatically delete torrents when they reach a given ratio (in options) : easy
|
|
||||||
- Allow to limit the number of downloading torrents simultaneously (other are paused until a download finishes)
|
|
||||||
- Add "Mark all as read" feature for RSS
|
|
||||||
- Allow to customize lists refreshing interval (in options)
|
|
||||||
- Use search engines as plugins (split them, load them dynamically) to allow the user to add some
|
|
||||||
|
|
||||||
// in v1.0.0 (partial) - WIP
|
|
||||||
- Check storage st creation + hasher in torrent creation
|
|
||||||
- Fix all (or almost all) opened bugs in bug tracker
|
|
||||||
- update sorting when a new torrent is added?
|
|
||||||
- Keep documention up to date
|
|
||||||
- Windows port (Chris - Peerkoel)
|
|
||||||
- write patches libtorrent for file_priority(int index), actual_size() ?
|
|
||||||
- valgrind --tool=memcheck --leak-check=full src/qbittorrent (Looks ok)
|
|
||||||
- 128m 29m 16m S 4.8 2.9 0:02.28 qbittorrent
|
|
||||||
* beta 6
|
|
||||||
- Translations update (IN PROGRESS)
|
|
||||||
- Wait for some bug fixes in libtorrent :
|
|
||||||
- Number of seeds non null for finished torrent (Ticket #122)
|
|
||||||
|
|
||||||
LANGUAGES UPDATED:
|
|
||||||
- French *BETA3*
|
|
||||||
- English *BETA3*
|
|
||||||
- Japanese *BETA3*
|
|
||||||
- Swedish *BETA3*
|
|
||||||
- Slovak *BETA3*
|
|
||||||
- Ukrainian *BETA3*
|
|
||||||
- Chinese (simplified) *BETA4*
|
|
||||||
- Hungarian *BETA4*
|
|
||||||
- Italian *BETA5*
|
|
||||||
- Polish *BETA5*
|
|
||||||
- Portuguese *BETA5*
|
|
||||||
- Brazilian *BETA5*
|
|
||||||
- Spanish *BETA5*
|
|
||||||
- German *BETA5*
|
|
||||||
- Russian *BETA5*
|
|
||||||
- Korean *BETA5*
|
|
||||||
- Greek *BETA6*
|
|
||||||
- Dutch *BETA6*
|
|
||||||
- Romanian *BETA6*
|
|
||||||
|
|
||||||
beta5->beta6 changelog:
|
|
||||||
- FEATURE: Split download tab from GUI class and cleaned up code
|
|
||||||
- FEATURE: A lot of code optimization (CPU & memory usage)
|
|
||||||
- FEATURE: Added support for .ico format (useful for RSS favicons)
|
|
||||||
- FEATURE: Replaced Meganova search engine by TorrentReactor
|
|
||||||
- I18N: Updated Greek, Dutch and Romanian translation
|
|
||||||
- I18N: Removed no longer maintained Traditional chinese translation
|
|
||||||
- BUGFIX: Made torrent deletion from hard-drive safer
|
|
||||||
- BUGFIX: Fixed a bug when switching from finished to downloading list
|
|
||||||
- BUGFIX: Showing checking progress for paused torrents too
|
|
||||||
- BUGFIX: Fixed progress column sorting on startup
|
|
||||||
- BUGFIX: Prevent downloadFromUrl flooding
|
|
||||||
- BUGFIX: Fixed pause state toggle for paused and checking torrents
|
|
||||||
- BUGFIX: Made finished list context menu more similar to the download list one
|
|
||||||
- BUGFIX: Fixed Pause/Start action in lists context menus
|
|
||||||
- BUGFIX: Improved ETA calculation
|
|
||||||
- BUGFIX: ETA was wrong for torrents with filtered files
|
|
||||||
- BUGFIX: Display the torrent that are being checked as 'checking' in seeding list
|
|
||||||
- BUGFIX: Fixed file preview and improved previewable files detection
|
|
||||||
- BUGFIX: Do not store and calculate ETA values for finished/paused torrents
|
|
||||||
- BUGFIX: Fixed memory leak in GUI
|
|
||||||
|
|||||||
199
configure
vendored
@@ -22,11 +22,14 @@ Dependency options:
|
|||||||
--with-libtorrent-lib=[path] Path to libtorrent library files
|
--with-libtorrent-lib=[path] Path to libtorrent library files
|
||||||
--with-libtorrent-static-lib=[path] Path to libtorrent .a file
|
--with-libtorrent-static-lib=[path] Path to libtorrent .a file
|
||||||
--with-libboost-inc=[path] Path to libboost include files
|
--with-libboost-inc=[path] Path to libboost include files
|
||||||
--with-libcommoncpp2-inc=[path] Path to libcommoncpp2 include files
|
--with-libcurl-inc=[path] Path to libcurl include files
|
||||||
--with-libcommoncpp2-lib=[path] Path to libcommoncpp2 library files
|
--with-libcurl-lib=[path] Path to libcurl library files
|
||||||
--disable-libmagick Disable use of libmagick
|
--disable-libmagick Disable use of libmagick
|
||||||
--with-libmagick-inc=[path] Path to libmagick++ include files
|
--with-libmagick-inc=[path] Path to libmagick++ include files
|
||||||
--with-libmagick-lib=[path] Path to libmagick++ library files
|
--with-libmagick-lib=[path] Path to libmagick++ library files
|
||||||
|
--disable-libzzip Disable use of libzzip
|
||||||
|
--with-libzzip-inc=[path] Path to libzzip++ include files
|
||||||
|
--with-libzzip-lib=[path] Path to libzzip++ library files
|
||||||
|
|
||||||
EOT
|
EOT
|
||||||
}
|
}
|
||||||
@@ -163,13 +166,13 @@ while [ $# -gt 0 ]; do
|
|||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
--with-libcommoncpp2-inc=*)
|
--with-libcurl-inc=*)
|
||||||
QC_WITH_LIBCOMMONCPP2_INC=$optarg
|
QC_WITH_LIBCURL_INC=$optarg
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
--with-libcommoncpp2-lib=*)
|
--with-libcurl-lib=*)
|
||||||
QC_WITH_LIBCOMMONCPP2_LIB=$optarg
|
QC_WITH_LIBCURL_LIB=$optarg
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@@ -188,6 +191,21 @@ while [ $# -gt 0 ]; do
|
|||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
--disable-libzzip)
|
||||||
|
QC_DISABLE_libzzip="Y"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
--with-libzzip-inc=*)
|
||||||
|
QC_WITH_LIBZZIP_INC=$optarg
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
--with-libzzip-lib=*)
|
||||||
|
QC_WITH_LIBZZIP_LIB=$optarg
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
--verbose)
|
--verbose)
|
||||||
QC_VERBOSE="Y"
|
QC_VERBOSE="Y"
|
||||||
shift
|
shift
|
||||||
@@ -213,11 +231,14 @@ echo QC_WITH_LIBTORRENT_INC=$QC_WITH_LIBTORRENT_INC
|
|||||||
echo QC_WITH_LIBTORRENT_LIB=$QC_WITH_LIBTORRENT_LIB
|
echo QC_WITH_LIBTORRENT_LIB=$QC_WITH_LIBTORRENT_LIB
|
||||||
echo QC_WITH_LIBTORRENT_STATIC_LIB=$QC_WITH_LIBTORRENT_STATIC_LIB
|
echo QC_WITH_LIBTORRENT_STATIC_LIB=$QC_WITH_LIBTORRENT_STATIC_LIB
|
||||||
echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC
|
echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC
|
||||||
echo QC_WITH_LIBCOMMONCPP2_INC=$QC_WITH_LIBCOMMONCPP2_INC
|
echo QC_WITH_LIBCURL_INC=$QC_WITH_LIBCURL_INC
|
||||||
echo QC_WITH_LIBCOMMONCPP2_LIB=$QC_WITH_LIBCOMMONCPP2_LIB
|
echo QC_WITH_LIBCURL_LIB=$QC_WITH_LIBCURL_LIB
|
||||||
echo QC_DISABLE_libmagick=$QC_DISABLE_libmagick
|
echo QC_DISABLE_libmagick=$QC_DISABLE_libmagick
|
||||||
echo QC_WITH_LIBMAGICK_INC=$QC_WITH_LIBMAGICK_INC
|
echo QC_WITH_LIBMAGICK_INC=$QC_WITH_LIBMAGICK_INC
|
||||||
echo QC_WITH_LIBMAGICK_LIB=$QC_WITH_LIBMAGICK_LIB
|
echo QC_WITH_LIBMAGICK_LIB=$QC_WITH_LIBMAGICK_LIB
|
||||||
|
echo QC_DISABLE_libzzip=$QC_DISABLE_libzzip
|
||||||
|
echo QC_WITH_LIBZZIP_INC=$QC_WITH_LIBZZIP_INC
|
||||||
|
echo QC_WITH_LIBZZIP_LIB=$QC_WITH_LIBZZIP_LIB
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -319,21 +340,21 @@ fi
|
|||||||
|
|
||||||
gen_files() {
|
gen_files() {
|
||||||
cat >$1/modules.cpp <<EOT
|
cat >$1/modules.cpp <<EOT
|
||||||
#line 1 "qt42.qcm"
|
#line 1 "qt4.qcm"
|
||||||
/*
|
/*
|
||||||
-----BEGIN QCMOD-----
|
-----BEGIN QCMOD-----
|
||||||
name: Qt >= 4.2
|
name: Qt >= 4.3
|
||||||
-----END QCMOD-----
|
-----END QCMOD-----
|
||||||
*/
|
*/
|
||||||
class qc_qt42 : public ConfObj
|
class qc_qt4 : public ConfObj
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
qc_qt42(Conf *c) : ConfObj(c) {}
|
qc_qt4(Conf *c) : ConfObj(c) {}
|
||||||
QString name() const { return "Qt >= 4.2"; }
|
QString name() const { return "Qt >= 4.3"; }
|
||||||
QString shortname() const { return "qt42"; }
|
QString shortname() const { return "Qt 4.3"; }
|
||||||
bool exec()
|
bool exec()
|
||||||
{
|
{
|
||||||
return(QT_VERSION >= 0x040200);
|
return(QT_VERSION >= 0x040300);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#line 1 "libtorrent.qcm"
|
#line 1 "libtorrent.qcm"
|
||||||
@@ -391,7 +412,9 @@ public:
|
|||||||
}else{
|
}else{
|
||||||
QStringList sl;
|
QStringList sl;
|
||||||
sl << "/usr/lib/";
|
sl << "/usr/lib/";
|
||||||
|
sl << "/usr/lib64/";
|
||||||
sl << "/usr/local/lib/";
|
sl << "/usr/local/lib/";
|
||||||
|
sl << "/usr/local/lib64/";
|
||||||
bool found = false;
|
bool found = false;
|
||||||
foreach(s, sl){
|
foreach(s, sl){
|
||||||
if(conf->checkLibrary(s, "torrent")){
|
if(conf->checkLibrary(s, "torrent")){
|
||||||
@@ -402,16 +425,6 @@ public:
|
|||||||
if(!found) return false;
|
if(!found) return false;
|
||||||
conf->addLib(QString("-L") + s);
|
conf->addLib(QString("-L") + s);
|
||||||
}
|
}
|
||||||
// BUGFIX for Fedora (doesn't support pkg-config?)
|
|
||||||
QFile issue_file("/etc/issue");
|
|
||||||
if(issue_file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
|
||||||
QString content = issue_file.readAll();
|
|
||||||
issue_file.close();
|
|
||||||
if(content.indexOf("Fedora") != -1){
|
|
||||||
qWarning("Fedora detected. WORKAROUND for Fedora pkg-config problem enabled");
|
|
||||||
conf->addLib("-lssl -lcrypto -lboost_date_time -lboost_filesystem -lboost_thread -lz -ltorrent");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -472,25 +485,25 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#line 1 "libcommoncpp2.qcm"
|
#line 1 "libcurl.qcm"
|
||||||
/*
|
/*
|
||||||
-----BEGIN QCMOD-----
|
-----BEGIN QCMOD-----
|
||||||
name: libcommoncpp2
|
name: libcommoncpp2
|
||||||
arg: with-libcommoncpp2-inc=[path], Path to libcommoncpp2 include files
|
arg: with-libcurl-inc=[path], Path to libcurl include files
|
||||||
arg: with-libcommoncpp2-lib=[path], Path to libcommoncpp2 library files
|
arg: with-libcurl-lib=[path], Path to libcurl library files
|
||||||
-----END QCMOD-----
|
-----END QCMOD-----
|
||||||
*/
|
*/
|
||||||
class qc_libcommoncpp2 : public ConfObj
|
class qc_libcurl : public ConfObj
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
qc_libcommoncpp2(Conf *c) : ConfObj(c) {}
|
qc_libcurl(Conf *c) : ConfObj(c) {}
|
||||||
QString name() const { return "GNU Common C++ library (libcommoncpp2)"; }
|
QString name() const { return "libcurl"; }
|
||||||
QString shortname() const { return "libcommoncpp2"; }
|
QString shortname() const { return "libcurl"; }
|
||||||
bool exec(){
|
bool exec(){
|
||||||
QString s;
|
QString s;
|
||||||
s = conf->getenv("QC_WITH_LIBCOMMONCPP2_INC");
|
s = conf->getenv("QC_WITH_LIBCURL_INC");
|
||||||
if(!s.isEmpty()) {
|
if(!s.isEmpty()) {
|
||||||
if(!conf->checkHeader(s, "cc++/url.h")) {
|
if(!conf->checkHeader(s, "curl/curl.h")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
@@ -499,7 +512,7 @@ public:
|
|||||||
sl << "/usr/local/include";
|
sl << "/usr/local/include";
|
||||||
bool found = false;
|
bool found = false;
|
||||||
foreach(s, sl){
|
foreach(s, sl){
|
||||||
if(conf->checkHeader(s, "cc++/url.h")){
|
if(conf->checkHeader(s, "curl/curl.h")){
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -510,38 +523,26 @@ public:
|
|||||||
}
|
}
|
||||||
conf->addIncludePath(s);
|
conf->addIncludePath(s);
|
||||||
|
|
||||||
s = conf->getenv("QC_WITH_LIBCOMMONCPP2_LIB");
|
s = conf->getenv("QC_WITH_LIBCURL_LIB");
|
||||||
if(!s.isEmpty()) {
|
if(!s.isEmpty()) {
|
||||||
if(!QFile::exists(s+QString("libccext2.so")))
|
if(!QFile::exists(s+QString("/libcurl.so")))
|
||||||
return false;
|
|
||||||
if(!QFile::exists(s+QString("libccgnu2.so")))
|
|
||||||
return false;
|
return false;
|
||||||
conf->addLib(QString("-L") + s);
|
conf->addLib(QString("-L") + s);
|
||||||
}else{
|
}else{
|
||||||
QStringList sl;
|
QStringList sl;
|
||||||
sl << "/usr/lib/";
|
sl << "/usr/lib/";
|
||||||
|
sl << "/usr/lib64/";
|
||||||
sl << "/usr/local/lib/";
|
sl << "/usr/local/lib/";
|
||||||
|
sl << "/usr/local/lib64/";
|
||||||
bool found = false;
|
bool found = false;
|
||||||
foreach(s, sl){
|
foreach(s, sl){
|
||||||
if(QFile::exists(s+QString("libccext2.so"))){
|
if(QFile::exists(s+QString("libcurl.so"))){
|
||||||
if(QFile::exists(s+QString("libccgnu2.so"))){
|
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if(!found) return false;
|
if(!found) return false;
|
||||||
conf->addLib(QString("-L") + s);
|
conf->addLib(QString("-L") + s);
|
||||||
}
|
|
||||||
// BUGFIX for Fedora (doesn't support pkg-config?)
|
|
||||||
QFile issue_file("/etc/issue");
|
|
||||||
if(issue_file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
|
||||||
QString content = issue_file.readAll();
|
|
||||||
issue_file.close();
|
|
||||||
if(content.indexOf("Fedora") != -1){
|
|
||||||
qWarning("Fedora detected. WORKAROUND for Fedora pkg-config problem enabled");
|
|
||||||
conf->addLib("-pthread -lccext2 -lz -lccgnu2 -ldl -lrt");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -593,16 +594,18 @@ public:
|
|||||||
|
|
||||||
s = conf->getenv("QC_WITH_LIBMAGICK_LIB");
|
s = conf->getenv("QC_WITH_LIBMAGICK_LIB");
|
||||||
if(!s.isEmpty()) {
|
if(!s.isEmpty()) {
|
||||||
if(!QFile::exists(s+QString("libMagick++.so"))){
|
if(!conf->checkLibrary(s, "Magick++")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
QStringList sl;
|
QStringList sl;
|
||||||
sl << "/usr/lib/";
|
sl << "/usr/lib/";
|
||||||
|
sl << "/usr/lib64/";
|
||||||
sl << "/usr/local/lib/";
|
sl << "/usr/local/lib/";
|
||||||
|
sl << "/usr/local/lib64/";
|
||||||
bool found = false;
|
bool found = false;
|
||||||
foreach(s, sl){
|
foreach(s, sl){
|
||||||
if(QFile::exists(s+QString("libMagick++.so"))){
|
if(conf->checkLibrary(s, "Magick++")) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -618,35 +621,88 @@ public:
|
|||||||
magickConfig.waitForStarted();
|
magickConfig.waitForStarted();
|
||||||
magickConfig.waitForFinished();
|
magickConfig.waitForFinished();
|
||||||
QByteArray result = magickConfig.readAll();
|
QByteArray result = magickConfig.readAll();
|
||||||
|
result = result.replace("\n", "");
|
||||||
conf->addLib(result.data());
|
conf->addLib(result.data());
|
||||||
conf->addDefine("HAVE_MAGICK");
|
conf->addDefine("HAVE_MAGICK");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#line 1 "python.qcm"
|
#line 1 "libzzip.qcm"
|
||||||
/*
|
/*
|
||||||
-----BEGIN QCMOD-----
|
-----BEGIN QCMOD-----
|
||||||
name: python
|
name: libzzip
|
||||||
|
arg: with-libzzip-inc=[path], Path to libzzip++ include files
|
||||||
|
arg: with-libzzip-lib=[path], Path to libzzip++ library files
|
||||||
-----END QCMOD-----
|
-----END QCMOD-----
|
||||||
*/
|
*/
|
||||||
class qc_python : public ConfObj
|
#include <QProcess>
|
||||||
|
class qc_libzzip : public ConfObj
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
qc_python(Conf *c) : ConfObj(c) {}
|
qc_libzzip(Conf *c) : ConfObj(c) {}
|
||||||
QString name() const { return "python >= 2.3"; }
|
QString name() const { return "Zzip library (libzzip)"; }
|
||||||
QString shortname() const { return "python"; }
|
QString shortname() const { return "libzzip"; }
|
||||||
|
QString checkString() const {
|
||||||
|
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
|
||||||
|
return "";
|
||||||
|
return ConfObj::checkString();
|
||||||
|
}
|
||||||
bool exec(){
|
bool exec(){
|
||||||
int r = conf->doCommand("python testpython.py");
|
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
|
||||||
if(r == 0)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
return false;
|
||||||
|
QString s;
|
||||||
|
s = conf->getenv("QC_WITH_LIBZZIP_INC");
|
||||||
|
if(!s.isEmpty()) {
|
||||||
|
if(!conf->checkHeader(s, "zzip/zzip.h")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
QStringList sl;
|
||||||
|
sl << "/usr/include";
|
||||||
|
sl << "/usr/local/include";
|
||||||
|
bool found = false;
|
||||||
|
foreach(s, sl){
|
||||||
|
if(conf->checkHeader(s, "zzip/zzip.h")){
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
conf->addIncludePath(s);
|
||||||
|
|
||||||
|
s = conf->getenv("QC_WITH_LIBZZIP_LIB");
|
||||||
|
if(!s.isEmpty()) {
|
||||||
|
if(!QFile::exists(s+QString("/libzzip.so"))){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
QStringList sl;
|
||||||
|
sl << "/usr/lib/";
|
||||||
|
sl << "/usr/lib64/";
|
||||||
|
sl << "/usr/local/lib/";
|
||||||
|
sl << "/usr/local/lib64/";
|
||||||
|
bool found = false;
|
||||||
|
foreach(s, sl){
|
||||||
|
if(QFile::exists(s+QString("libzzip.so"))){
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
conf->addLib(QString("-L") + s);
|
||||||
|
conf->addLib("-lzzip");
|
||||||
|
conf->addDefine("HAVE_ZZIP");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EOT
|
EOT
|
||||||
cat >$1/modules_new.cpp <<EOT
|
cat >$1/modules_new.cpp <<EOT
|
||||||
o = new qc_qt42(conf);
|
o = new qc_qt4(conf);
|
||||||
o->required = true;
|
o->required = true;
|
||||||
o->disabled = false;
|
o->disabled = false;
|
||||||
o = new qc_libtorrent(conf);
|
o = new qc_libtorrent(conf);
|
||||||
@@ -655,14 +711,14 @@ cat >$1/modules_new.cpp <<EOT
|
|||||||
o = new qc_libboost(conf);
|
o = new qc_libboost(conf);
|
||||||
o->required = true;
|
o->required = true;
|
||||||
o->disabled = false;
|
o->disabled = false;
|
||||||
o = new qc_libcommoncpp2(conf);
|
o = new qc_libcurl(conf);
|
||||||
o->required = true;
|
o->required = true;
|
||||||
o->disabled = false;
|
o->disabled = false;
|
||||||
o = new qc_libmagick(conf);
|
o = new qc_libmagick(conf);
|
||||||
o->required = false;
|
o->required = false;
|
||||||
o->disabled = false;
|
o->disabled = false;
|
||||||
o = new qc_python(conf);
|
o = new qc_libzzip(conf);
|
||||||
o->required = true;
|
o->required = false;
|
||||||
o->disabled = false;
|
o->disabled = false;
|
||||||
|
|
||||||
EOT
|
EOT
|
||||||
@@ -1613,11 +1669,14 @@ export QC_WITH_LIBTORRENT_INC
|
|||||||
export QC_WITH_LIBTORRENT_LIB
|
export QC_WITH_LIBTORRENT_LIB
|
||||||
export QC_WITH_LIBTORRENT_STATIC_LIB
|
export QC_WITH_LIBTORRENT_STATIC_LIB
|
||||||
export QC_WITH_LIBBOOST_INC
|
export QC_WITH_LIBBOOST_INC
|
||||||
export QC_WITH_LIBCOMMONCPP2_INC
|
export QC_WITH_LIBCURL_INC
|
||||||
export QC_WITH_LIBCOMMONCPP2_LIB
|
export QC_WITH_LIBCURL_LIB
|
||||||
export QC_DISABLE_libmagick
|
export QC_DISABLE_libmagick
|
||||||
export QC_WITH_LIBMAGICK_INC
|
export QC_WITH_LIBMAGICK_INC
|
||||||
export QC_WITH_LIBMAGICK_LIB
|
export QC_WITH_LIBMAGICK_LIB
|
||||||
|
export QC_DISABLE_libzzip
|
||||||
|
export QC_WITH_LIBZZIP_INC
|
||||||
|
export QC_WITH_LIBZZIP_LIB
|
||||||
export QC_VERBOSE
|
export QC_VERBOSE
|
||||||
rm -rf .qconftemp
|
rm -rf .qconftemp
|
||||||
(
|
(
|
||||||
|
|||||||
28
doc/qbittorrent.1
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
.\" This manpage has been automatically generated by docbook2man
|
||||||
|
.\" from a DocBook document. This tool can be found at:
|
||||||
|
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>.
|
||||||
|
.TH "QBITTORRENT" "1" "September 1st 2007" "Bittorrent client written in C++ / Qt4" ""
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
qBittorrent \- a Bittorrent client written in C++ / Qt4
|
||||||
|
.SH SYNOPSIS
|
||||||
|
|
||||||
|
\fBqbittorrent\fR [FILE | URL] [FILE | URL...]
|
||||||
|
|
||||||
|
\fBqbittorrent\fR \-\-help
|
||||||
|
|
||||||
|
\fBqbittorrent\fR \-\-version
|
||||||
|
.PP
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
|
||||||
|
\fBqBittorrent\fR is an advanced Bittorrent client written in C++ / Qt4,
|
||||||
|
using the \fBrblibtorrent\fR library by Arvid Norberg. qBittorrent aims
|
||||||
|
to be a good alternative to all other bittorrent clients out there. qBittorrent
|
||||||
|
is fast, stable, light, it supports unicode and it provides a good integrated search engine.
|
||||||
|
It also comes with UPnP port forwarding / NAT-PMP, encryption (Azureus compatible), FAST extension (mainline) and PeX support (utorrent compatible).
|
||||||
|
|
||||||
|
Please report any problem to http://bugs.qbittorrent.org
|
||||||
|
.PP
|
||||||
|
.SH "AUTHOR"
|
||||||
|
|
||||||
|
Christophe Dumez <chris@qbittorrent.org>
|
||||||
@@ -94,13 +94,12 @@
|
|||||||
<includestyle>4</includestyle>
|
<includestyle>4</includestyle>
|
||||||
<designerintegration>ExternalDesigner</designerintegration>
|
<designerintegration>ExternalDesigner</designerintegration>
|
||||||
<root>/usr/lib/qt4</root>
|
<root>/usr/lib/qt4</root>
|
||||||
<qmake>/usr/bin/qmake-qt4</qmake>
|
<qmake>/usr/bin/qmake</qmake>
|
||||||
<designer>/usr/bin/designer-qt4</designer>
|
<designer>/usr/bin/designer</designer>
|
||||||
<designerpluginpaths/>
|
<designerpluginpaths/>
|
||||||
</qt>
|
</qt>
|
||||||
<references>
|
<references>
|
||||||
<pcs>automatic_%2Fhome%2Fchris%2Fqbittorrent_svn%2Ftrunk</pcs>
|
<pcs>automatic_%2Fhome%2Fishanarora%2Fprojects%2Fqbittorrent</pcs>
|
||||||
<pcs>Qt4</pcs>
|
|
||||||
</references>
|
</references>
|
||||||
<codecompletion>
|
<codecompletion>
|
||||||
<automaticCodeCompletion>true</automaticCodeCompletion>
|
<automaticCodeCompletion>true</automaticCodeCompletion>
|
||||||
@@ -149,14 +148,14 @@
|
|||||||
</kdevfilecreate>
|
</kdevfilecreate>
|
||||||
<kdevtrollproject>
|
<kdevtrollproject>
|
||||||
<general>
|
<general>
|
||||||
<activedir></activedir>
|
<activedir>src</activedir>
|
||||||
</general>
|
</general>
|
||||||
<run>
|
<run>
|
||||||
<directoryradio>executable</directoryradio>
|
<directoryradio>executable</directoryradio>
|
||||||
<mainprogram>/home/chris/qbittorrent_svn/trunk/src/qbittorrent</mainprogram>
|
<mainprogram>/home/ishanarora/projects/qbittorrent/src/qbittorrent</mainprogram>
|
||||||
<programargs/>
|
<programargs/>
|
||||||
<globaldebugarguments/>
|
<globaldebugarguments/>
|
||||||
<globalcwd>/home/chris/qbittorrent_svn/trunk</globalcwd>
|
<globalcwd>/home/ishanarora/projects/qbittorrent</globalcwd>
|
||||||
<useglobalprogram>true</useglobalprogram>
|
<useglobalprogram>true</useglobalprogram>
|
||||||
<terminal>false</terminal>
|
<terminal>false</terminal>
|
||||||
<autocompile>true</autocompile>
|
<autocompile>true</autocompile>
|
||||||
@@ -187,7 +186,7 @@
|
|||||||
</cppsupportpart>
|
</cppsupportpart>
|
||||||
<ctagspart>
|
<ctagspart>
|
||||||
<customArguments/>
|
<customArguments/>
|
||||||
<customTagfilePath>/home/chris/qbittorrent_svn/trunk/tags</customTagfilePath>
|
<customTagfilePath>/home/ishanarora/projects/qbittorrent/tags</customTagfilePath>
|
||||||
<activeTagsFiles/>
|
<activeTagsFiles/>
|
||||||
</ctagspart>
|
</ctagspart>
|
||||||
<kdevdocumentation>
|
<kdevdocumentation>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<profile>qbittorrent.pro</profile>
|
<profile>qbittorrent.pro</profile>
|
||||||
<moddir>qcm</moddir>
|
<moddir>qcm</moddir>
|
||||||
<datadir/>
|
<datadir/>
|
||||||
<dep type='qt42'>
|
<dep type='qt4'>
|
||||||
<required/>
|
<required/>
|
||||||
</dep>
|
</dep>
|
||||||
<dep type='libtorrent'>
|
<dep type='libtorrent'>
|
||||||
@@ -12,11 +12,9 @@
|
|||||||
<dep type='libboost'>
|
<dep type='libboost'>
|
||||||
<required/>
|
<required/>
|
||||||
</dep>
|
</dep>
|
||||||
<dep type='libcommoncpp2'>
|
<dep type='libcurl'>
|
||||||
<required/>
|
<required/>
|
||||||
</dep>
|
</dep>
|
||||||
<dep type='libmagick'/>
|
<dep type='libmagick'/>
|
||||||
<dep type='python'>
|
<dep type='libzzip'/>
|
||||||
<required/>
|
|
||||||
</dep>
|
|
||||||
</qconf>
|
</qconf>
|
||||||
|
|||||||
@@ -37,15 +37,17 @@ public:
|
|||||||
|
|
||||||
s = conf->getenv("QC_WITH_LIBCOMMONCPP2_LIB");
|
s = conf->getenv("QC_WITH_LIBCOMMONCPP2_LIB");
|
||||||
if(!s.isEmpty()) {
|
if(!s.isEmpty()) {
|
||||||
if(!QFile::exists(s+QString("libccext2.so")))
|
if(!QFile::exists(s+QString("/libccext2.so")))
|
||||||
return false;
|
return false;
|
||||||
if(!QFile::exists(s+QString("libccgnu2.so")))
|
if(!QFile::exists(s+QString("/libccgnu2.so")))
|
||||||
return false;
|
return false;
|
||||||
conf->addLib(QString("-L") + s);
|
conf->addLib(QString("-L") + s);
|
||||||
}else{
|
}else{
|
||||||
QStringList sl;
|
QStringList sl;
|
||||||
sl << "/usr/lib/";
|
sl << "/usr/lib/";
|
||||||
|
sl << "/usr/lib64/";
|
||||||
sl << "/usr/local/lib/";
|
sl << "/usr/local/lib/";
|
||||||
|
sl << "/usr/local/lib64/";
|
||||||
bool found = false;
|
bool found = false;
|
||||||
foreach(s, sl){
|
foreach(s, sl){
|
||||||
if(QFile::exists(s+QString("libccext2.so"))){
|
if(QFile::exists(s+QString("libccext2.so"))){
|
||||||
@@ -57,16 +59,6 @@ public:
|
|||||||
}
|
}
|
||||||
if(!found) return false;
|
if(!found) return false;
|
||||||
conf->addLib(QString("-L") + s);
|
conf->addLib(QString("-L") + s);
|
||||||
}
|
|
||||||
// BUGFIX for Fedora (doesn't support pkg-config?)
|
|
||||||
QFile issue_file("/etc/issue");
|
|
||||||
if(issue_file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
|
||||||
QString content = issue_file.readAll();
|
|
||||||
issue_file.close();
|
|
||||||
if(content.indexOf("Fedora") != -1){
|
|
||||||
qWarning("Fedora detected. WORKAROUND for Fedora pkg-config problem enabled");
|
|
||||||
conf->addLib("-pthread -lccext2 -lz -lccgnu2 -ldl -lrt");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
61
qcm/libcurl.qcm
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
-----BEGIN QCMOD-----
|
||||||
|
name: libcommoncpp2
|
||||||
|
arg: with-libcurl-inc=[path], Path to libcurl include files
|
||||||
|
arg: with-libcurl-lib=[path], Path to libcurl library files
|
||||||
|
-----END QCMOD-----
|
||||||
|
*/
|
||||||
|
class qc_libcurl : public ConfObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
qc_libcurl(Conf *c) : ConfObj(c) {}
|
||||||
|
QString name() const { return "libcurl"; }
|
||||||
|
QString shortname() const { return "libcurl"; }
|
||||||
|
bool exec(){
|
||||||
|
QString s;
|
||||||
|
s = conf->getenv("QC_WITH_LIBCURL_INC");
|
||||||
|
if(!s.isEmpty()) {
|
||||||
|
if(!conf->checkHeader(s, "curl/curl.h")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
QStringList sl;
|
||||||
|
sl << "/usr/include";
|
||||||
|
sl << "/usr/local/include";
|
||||||
|
bool found = false;
|
||||||
|
foreach(s, sl){
|
||||||
|
if(conf->checkHeader(s, "curl/curl.h")){
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conf->addIncludePath(s);
|
||||||
|
|
||||||
|
s = conf->getenv("QC_WITH_LIBCURL_LIB");
|
||||||
|
if(!s.isEmpty()) {
|
||||||
|
if(!QFile::exists(s+QString("/libcurl.so")))
|
||||||
|
return false;
|
||||||
|
conf->addLib(QString("-L") + s);
|
||||||
|
}else{
|
||||||
|
QStringList sl;
|
||||||
|
sl << "/usr/lib/";
|
||||||
|
sl << "/usr/lib64/";
|
||||||
|
sl << "/usr/local/lib/";
|
||||||
|
sl << "/usr/local/lib64/";
|
||||||
|
bool found = false;
|
||||||
|
foreach(s, sl){
|
||||||
|
if(QFile::exists(s+QString("libcurl.so"))){
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) return false;
|
||||||
|
conf->addLib(QString("-L") + s);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -44,16 +44,18 @@ public:
|
|||||||
|
|
||||||
s = conf->getenv("QC_WITH_LIBMAGICK_LIB");
|
s = conf->getenv("QC_WITH_LIBMAGICK_LIB");
|
||||||
if(!s.isEmpty()) {
|
if(!s.isEmpty()) {
|
||||||
if(!QFile::exists(s+QString("libMagick++.so"))){
|
if(!conf->checkLibrary(s, "Magick++")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
QStringList sl;
|
QStringList sl;
|
||||||
sl << "/usr/lib/";
|
sl << "/usr/lib/";
|
||||||
|
sl << "/usr/lib64/";
|
||||||
sl << "/usr/local/lib/";
|
sl << "/usr/local/lib/";
|
||||||
|
sl << "/usr/local/lib64/";
|
||||||
bool found = false;
|
bool found = false;
|
||||||
foreach(s, sl){
|
foreach(s, sl){
|
||||||
if(QFile::exists(s+QString("libMagick++.so"))){
|
if(conf->checkLibrary(s, "Magick++")) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -69,6 +71,7 @@ public:
|
|||||||
magickConfig.waitForStarted();
|
magickConfig.waitForStarted();
|
||||||
magickConfig.waitForFinished();
|
magickConfig.waitForFinished();
|
||||||
QByteArray result = magickConfig.readAll();
|
QByteArray result = magickConfig.readAll();
|
||||||
|
result = result.replace("\n", "");
|
||||||
conf->addLib(result.data());
|
conf->addLib(result.data());
|
||||||
conf->addDefine("HAVE_MAGICK");
|
conf->addDefine("HAVE_MAGICK");
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ public:
|
|||||||
}else{
|
}else{
|
||||||
QStringList sl;
|
QStringList sl;
|
||||||
sl << "/usr/lib/";
|
sl << "/usr/lib/";
|
||||||
|
sl << "/usr/lib64/";
|
||||||
sl << "/usr/local/lib/";
|
sl << "/usr/local/lib/";
|
||||||
|
sl << "/usr/local/lib64/";
|
||||||
bool found = false;
|
bool found = false;
|
||||||
foreach(s, sl){
|
foreach(s, sl){
|
||||||
if(conf->checkLibrary(s, "torrent")){
|
if(conf->checkLibrary(s, "torrent")){
|
||||||
@@ -63,16 +65,6 @@ public:
|
|||||||
if(!found) return false;
|
if(!found) return false;
|
||||||
conf->addLib(QString("-L") + s);
|
conf->addLib(QString("-L") + s);
|
||||||
}
|
}
|
||||||
// BUGFIX for Fedora (doesn't support pkg-config?)
|
|
||||||
QFile issue_file("/etc/issue");
|
|
||||||
if(issue_file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
|
||||||
QString content = issue_file.readAll();
|
|
||||||
issue_file.close();
|
|
||||||
if(content.indexOf("Fedora") != -1){
|
|
||||||
qWarning("Fedora detected. WORKAROUND for Fedora pkg-config problem enabled");
|
|
||||||
conf->addLib("-lssl -lcrypto -lboost_date_time -lboost_filesystem -lboost_thread -lz -ltorrent");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
71
qcm/libzzip.qcm
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
-----BEGIN QCMOD-----
|
||||||
|
name: libzzip
|
||||||
|
arg: with-libzzip-inc=[path], Path to libzzip++ include files
|
||||||
|
arg: with-libzzip-lib=[path], Path to libzzip++ library files
|
||||||
|
-----END QCMOD-----
|
||||||
|
*/
|
||||||
|
#include <QProcess>
|
||||||
|
class qc_libzzip : public ConfObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
qc_libzzip(Conf *c) : ConfObj(c) {}
|
||||||
|
QString name() const { return "Zzip library (libzzip)"; }
|
||||||
|
QString shortname() const { return "libzzip"; }
|
||||||
|
QString checkString() const {
|
||||||
|
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
|
||||||
|
return "";
|
||||||
|
return ConfObj::checkString();
|
||||||
|
}
|
||||||
|
bool exec(){
|
||||||
|
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
|
||||||
|
return false;
|
||||||
|
QString s;
|
||||||
|
s = conf->getenv("QC_WITH_LIBZZIP_INC");
|
||||||
|
if(!s.isEmpty()) {
|
||||||
|
if(!conf->checkHeader(s, "zzip/zzip.h")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
QStringList sl;
|
||||||
|
sl << "/usr/include";
|
||||||
|
sl << "/usr/local/include";
|
||||||
|
bool found = false;
|
||||||
|
foreach(s, sl){
|
||||||
|
if(conf->checkHeader(s, "zzip/zzip.h")){
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
conf->addIncludePath(s);
|
||||||
|
|
||||||
|
s = conf->getenv("QC_WITH_LIBZZIP_LIB");
|
||||||
|
if(!s.isEmpty()) {
|
||||||
|
if(!QFile::exists(s+QString("/libzzip.so"))){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
QStringList sl;
|
||||||
|
sl << "/usr/lib/";
|
||||||
|
sl << "/usr/lib64/";
|
||||||
|
sl << "/usr/local/lib/";
|
||||||
|
sl << "/usr/local/lib64/";
|
||||||
|
bool found = false;
|
||||||
|
foreach(s, sl){
|
||||||
|
if(QFile::exists(s+QString("libzzip.so"))){
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
conf->addLib(QString("-L") + s);
|
||||||
|
conf->addLib("-lzzip");
|
||||||
|
conf->addDefine("HAVE_ZZIP");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/*
|
|
||||||
-----BEGIN QCMOD-----
|
|
||||||
name: python
|
|
||||||
-----END QCMOD-----
|
|
||||||
*/
|
|
||||||
class qc_python : public ConfObj
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
qc_python(Conf *c) : ConfObj(c) {}
|
|
||||||
QString name() const { return "python >= 2.3"; }
|
|
||||||
QString shortname() const { return "python"; }
|
|
||||||
bool exec(){
|
|
||||||
int r = conf->doCommand("python testpython.py");
|
|
||||||
if(r == 0)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
16
qcm/qt4.qcm
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
-----BEGIN QCMOD-----
|
||||||
|
name: Qt >= 4.3
|
||||||
|
-----END QCMOD-----
|
||||||
|
*/
|
||||||
|
class qc_qt4 : public ConfObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
qc_qt4(Conf *c) : ConfObj(c) {}
|
||||||
|
QString name() const { return "Qt >= 4.3"; }
|
||||||
|
QString shortname() const { return "Qt 4.3"; }
|
||||||
|
bool exec()
|
||||||
|
{
|
||||||
|
return(QT_VERSION >= 0x040300);
|
||||||
|
}
|
||||||
|
};
|
||||||
16
qcm/qt42.qcm
@@ -1,16 +0,0 @@
|
|||||||
/*
|
|
||||||
-----BEGIN QCMOD-----
|
|
||||||
name: Qt >= 4.2
|
|
||||||
-----END QCMOD-----
|
|
||||||
*/
|
|
||||||
class qc_qt42 : public ConfObj
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
qc_qt42(Conf *c) : ConfObj(c) {}
|
|
||||||
QString name() const { return "Qt >= 4.2"; }
|
|
||||||
QString shortname() const { return "qt42"; }
|
|
||||||
bool exec()
|
|
||||||
{
|
|
||||||
return(QT_VERSION >= 0x040200);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -83,11 +83,9 @@ class DLListDelegate: public QItemDelegate {
|
|||||||
newopt.maximum = 100;
|
newopt.maximum = 100;
|
||||||
newopt.minimum = 0;
|
newopt.minimum = 0;
|
||||||
newopt.state |= QStyle::State_Enabled;
|
newopt.state |= QStyle::State_Enabled;
|
||||||
newopt.textVisible = false;
|
newopt.textVisible = true;
|
||||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt,
|
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt,
|
||||||
painter);
|
painter);
|
||||||
painter->setPen(QColor("Black"));
|
|
||||||
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -34,11 +34,10 @@
|
|||||||
// Defines for download list list columns
|
// Defines for download list list columns
|
||||||
#define F_NAME 0
|
#define F_NAME 0
|
||||||
#define F_SIZE 1
|
#define F_SIZE 1
|
||||||
#define F_PROGRESS 2
|
#define F_UPSPEED 2
|
||||||
#define F_UPSPEED 3
|
#define F_LEECH 3
|
||||||
#define F_SEEDSLEECH 4
|
#define F_RATIO 4
|
||||||
#define F_RATIO 5
|
#define F_HASH 5
|
||||||
#define F_HASH 6
|
|
||||||
|
|
||||||
class FinishedListDelegate: public QItemDelegate {
|
class FinishedListDelegate: public QItemDelegate {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -67,22 +66,6 @@ class FinishedListDelegate: public QItemDelegate {
|
|||||||
QItemDelegate::drawDisplay(painter, opt, opt.rect, QString(QByteArray::number(ratio, 'f', 1)));
|
QItemDelegate::drawDisplay(painter, opt, opt.rect, QString(QByteArray::number(ratio, 'f', 1)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case F_PROGRESS:{
|
|
||||||
QStyleOptionProgressBarV2 newopt;
|
|
||||||
double progress = index.data().toDouble()*100.;
|
|
||||||
newopt.rect = opt.rect;
|
|
||||||
newopt.text = QString(QByteArray::number(progress, 'f', 1))+QString::fromUtf8("%");
|
|
||||||
newopt.progress = (int)progress;
|
|
||||||
newopt.maximum = 100;
|
|
||||||
newopt.minimum = 0;
|
|
||||||
newopt.state |= QStyle::State_Enabled;
|
|
||||||
newopt.textVisible = false;
|
|
||||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt,
|
|
||||||
painter);
|
|
||||||
painter->setPen(QColor("Black"));
|
|
||||||
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
QItemDelegate::paint(painter, option, index);
|
QItemDelegate::paint(painter, option, index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,15 +37,15 @@ FinishedTorrents::FinishedTorrents(QObject *parent, bittorrent *BTSession) : par
|
|||||||
actionStart->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/play.png")));
|
actionStart->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/play.png")));
|
||||||
actionPause->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/pause.png")));
|
actionPause->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/pause.png")));
|
||||||
connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), this, SLOT(torrentAdded(QString, QTorrentHandle&, bool)));
|
connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), this, SLOT(torrentAdded(QString, QTorrentHandle&, bool)));
|
||||||
finishedListModel = new QStandardItemModel(0,7);
|
finishedListModel = new QStandardItemModel(0,6);
|
||||||
finishedListModel->setHeaderData(F_NAME, Qt::Horizontal, tr("Name", "i.e: file name"));
|
finishedListModel->setHeaderData(F_NAME, Qt::Horizontal, tr("Name", "i.e: file name"));
|
||||||
finishedListModel->setHeaderData(F_SIZE, Qt::Horizontal, tr("Size", "i.e: file size"));
|
finishedListModel->setHeaderData(F_SIZE, Qt::Horizontal, tr("Size", "i.e: file size"));
|
||||||
finishedListModel->setHeaderData(F_PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded"));
|
|
||||||
finishedListModel->setHeaderData(F_UPSPEED, Qt::Horizontal, tr("UP Speed", "i.e: Upload speed"));
|
finishedListModel->setHeaderData(F_UPSPEED, Qt::Horizontal, tr("UP Speed", "i.e: Upload speed"));
|
||||||
finishedListModel->setHeaderData(F_SEEDSLEECH, Qt::Horizontal, tr("Seeds/Leechs", "i.e: full/partial sources"));
|
finishedListModel->setHeaderData(F_LEECH, Qt::Horizontal, tr("Leechers", "i.e: full/partial sources"));
|
||||||
finishedListModel->setHeaderData(F_RATIO, Qt::Horizontal, tr("Ratio"));
|
finishedListModel->setHeaderData(F_RATIO, Qt::Horizontal, tr("Ratio"));
|
||||||
finishedList->setModel(finishedListModel);
|
finishedList->setModel(finishedListModel);
|
||||||
// Hide ETA & hash column
|
loadHiddenColumns();
|
||||||
|
// Hide hash column
|
||||||
finishedList->hideColumn(F_HASH);
|
finishedList->hideColumn(F_HASH);
|
||||||
// Load last columns width for download list
|
// Load last columns width for download list
|
||||||
if(!loadColWidthFinishedList()){
|
if(!loadColWidthFinishedList()){
|
||||||
@@ -58,6 +58,9 @@ FinishedTorrents::FinishedTorrents(QObject *parent, bittorrent *BTSession) : par
|
|||||||
finishedListDelegate = new FinishedListDelegate(finishedList);
|
finishedListDelegate = new FinishedListDelegate(finishedList);
|
||||||
finishedList->setItemDelegate(finishedListDelegate);
|
finishedList->setItemDelegate(finishedListDelegate);
|
||||||
connect(finishedList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFinishedListMenu(const QPoint&)));
|
connect(finishedList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFinishedListMenu(const QPoint&)));
|
||||||
|
finishedList->header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(finishedList->header(), SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFinishedHoSMenu(const QPoint&)));
|
||||||
|
|
||||||
connect(finishedList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(notifyTorrentDoubleClicked(const QModelIndex&)));
|
connect(finishedList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(notifyTorrentDoubleClicked(const QModelIndex&)));
|
||||||
actionDelete->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/delete.png")));
|
actionDelete->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/delete.png")));
|
||||||
actionPreview_file->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/preview.png")));
|
actionPreview_file->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/preview.png")));
|
||||||
@@ -69,11 +72,20 @@ FinishedTorrents::FinishedTorrents(QObject *parent, bittorrent *BTSession) : par
|
|||||||
connect(actionDelete, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_triggered()));
|
connect(actionDelete, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_triggered()));
|
||||||
connect(actionPreview_file, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPreview_file_triggered()));
|
connect(actionPreview_file, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPreview_file_triggered()));
|
||||||
connect(actionDelete_Permanently, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_Permanently_triggered()));
|
connect(actionDelete_Permanently, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_Permanently_triggered()));
|
||||||
|
connect(actionOpen_destination_folder, SIGNAL(triggered()), (GUI*)parent, SLOT(openDestinationFolder()));
|
||||||
|
connect(actionBuy_it, SIGNAL(triggered()), (GUI*)parent, SLOT(goBuyPage()));
|
||||||
connect(actionTorrent_Properties, SIGNAL(triggered()), this, SLOT(propertiesSelection()));
|
connect(actionTorrent_Properties, SIGNAL(triggered()), this, SLOT(propertiesSelection()));
|
||||||
|
|
||||||
|
connect(actionHOSColName, SIGNAL(triggered()), this, SLOT(hideOrShowColumnName()));
|
||||||
|
connect(actionHOSColSize, SIGNAL(triggered()), this, SLOT(hideOrShowColumnSize()));
|
||||||
|
connect(actionHOSColUpSpeed, SIGNAL(triggered()), this, SLOT(hideOrShowColumnUpSpeed()));
|
||||||
|
connect(actionHOSColLeechers, SIGNAL(triggered()), this, SLOT(hideOrShowColumnLeechers()));
|
||||||
|
connect(actionHOSColRatio, SIGNAL(triggered()), this, SLOT(hideOrShowColumnRatio()));
|
||||||
}
|
}
|
||||||
|
|
||||||
FinishedTorrents::~FinishedTorrents(){
|
FinishedTorrents::~FinishedTorrents(){
|
||||||
saveColWidthFinishedList();
|
saveColWidthFinishedList();
|
||||||
|
saveHiddenColumns();
|
||||||
delete finishedListDelegate;
|
delete finishedListDelegate;
|
||||||
delete finishedListModel;
|
delete finishedListModel;
|
||||||
}
|
}
|
||||||
@@ -81,7 +93,7 @@ FinishedTorrents::~FinishedTorrents(){
|
|||||||
void FinishedTorrents::notifyTorrentDoubleClicked(const QModelIndex& index) {
|
void FinishedTorrents::notifyTorrentDoubleClicked(const QModelIndex& index) {
|
||||||
unsigned int row = index.row();
|
unsigned int row = index.row();
|
||||||
QString hash = getHashFromRow(row);
|
QString hash = getHashFromRow(row);
|
||||||
emit torrentDoubleClicked(hash);
|
emit torrentDoubleClicked(hash, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FinishedTorrents::addTorrent(QString hash){
|
void FinishedTorrents::addTorrent(QString hash){
|
||||||
@@ -97,10 +109,9 @@ void FinishedTorrents::addTorrent(QString hash){
|
|||||||
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(h.name()));
|
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(h.name()));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_SIZE), QVariant((qlonglong)h.actual_size()));
|
finishedListModel->setData(finishedListModel->index(row, F_SIZE), QVariant((qlonglong)h.actual_size()));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)0.));
|
finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)0.));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_SEEDSLEECH), QVariant("0/0"));
|
finishedListModel->setData(finishedListModel->index(row, F_LEECH), QVariant("0"));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_RATIO), QVariant(QString::fromUtf8(misc::toString(BTSession->getRealRatio(hash)).c_str())));
|
finishedListModel->setData(finishedListModel->index(row, F_RATIO), QVariant(QString::fromUtf8(misc::toString(BTSession->getRealRatio(hash)).c_str())));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_HASH), QVariant(hash));
|
finishedListModel->setData(finishedListModel->index(row, F_HASH), QVariant(hash));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)1.));
|
|
||||||
if(h.is_paused()) {
|
if(h.is_paused()) {
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_NAME), QIcon(":/Icons/skin/paused.png"), Qt::DecorationRole);
|
finishedListModel->setData(finishedListModel->index(row, F_NAME), QIcon(":/Icons/skin/paused.png"), Qt::DecorationRole);
|
||||||
setRowColor(row, "red");
|
setRowColor(row, "red");
|
||||||
@@ -122,7 +133,8 @@ void FinishedTorrents::torrentAdded(QString, QTorrentHandle& h, bool) {
|
|||||||
|
|
||||||
// Set the color of a row in data model
|
// Set the color of a row in data model
|
||||||
void FinishedTorrents::setRowColor(int row, QString color){
|
void FinishedTorrents::setRowColor(int row, QString color){
|
||||||
for(int i=0; i<finishedListModel->columnCount(); ++i){
|
unsigned int nbColumns = finishedListModel->columnCount()-1;
|
||||||
|
for(unsigned int i=0; i<nbColumns; ++i){
|
||||||
finishedListModel->setData(finishedListModel->index(row, i), QVariant(QColor(color)), Qt::ForegroundRole);
|
finishedListModel->setData(finishedListModel->index(row, i), QVariant(QColor(color)), Qt::ForegroundRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,7 +167,7 @@ bool FinishedTorrents::loadColWidthFinishedList(){
|
|||||||
if(line.isEmpty())
|
if(line.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
QStringList width_list = line.split(' ');
|
QStringList width_list = line.split(' ');
|
||||||
if(width_list.size() != finishedListModel->columnCount()-1)
|
if(width_list.size() < finishedListModel->columnCount()-1)
|
||||||
return false;
|
return false;
|
||||||
unsigned int listSize = width_list.size();
|
unsigned int listSize = width_list.size();
|
||||||
for(unsigned int i=0; i<listSize; ++i){
|
for(unsigned int i=0; i<listSize; ++i){
|
||||||
@@ -171,11 +183,27 @@ void FinishedTorrents::saveColWidthFinishedList() const{
|
|||||||
qDebug("Saving columns width in finished list");
|
qDebug("Saving columns width in finished list");
|
||||||
QSettings settings("qBittorrent", "qBittorrent");
|
QSettings settings("qBittorrent", "qBittorrent");
|
||||||
QStringList width_list;
|
QStringList width_list;
|
||||||
unsigned int nbColumns = finishedListModel->columnCount()-1;
|
QStringList new_width_list;
|
||||||
for(unsigned int i=0; i<nbColumns; ++i){
|
short nbColumns = finishedListModel->columnCount()-1;
|
||||||
width_list << QString::fromUtf8(misc::toString(finishedList->columnWidth(i)).c_str());
|
|
||||||
|
QString line = settings.value("FinishedListColsWidth", QString()).toString();
|
||||||
|
if(!line.isEmpty()) {
|
||||||
|
width_list = line.split(' ');
|
||||||
}
|
}
|
||||||
settings.setValue("FinishedListColsWidth", width_list.join(" "));
|
for(short i=0; i<nbColumns; ++i){
|
||||||
|
if(finishedList->columnWidth(i)<1 && width_list.size() == finishedListModel->columnCount()-1 && width_list.at(i).toInt()>=1) {
|
||||||
|
// load the former width
|
||||||
|
new_width_list << width_list.at(i);
|
||||||
|
} else if(finishedList->columnWidth(i)>=1) {
|
||||||
|
// usual case, save the current width
|
||||||
|
new_width_list << QString::fromUtf8(misc::toString(finishedList->columnWidth(i)).c_str());
|
||||||
|
} else {
|
||||||
|
// default width
|
||||||
|
finishedList->resizeColumnToContents(i);
|
||||||
|
new_width_list << QString::fromUtf8(misc::toString(finishedList->columnWidth(i)).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.setValue("FinishedListColsWidth", new_width_list.join(" "));
|
||||||
qDebug("Finished list columns width saved");
|
qDebug("Finished list columns width saved");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +237,9 @@ void FinishedTorrents::updateFinishedList(){
|
|||||||
}
|
}
|
||||||
Q_ASSERT(row != -1);
|
Q_ASSERT(row != -1);
|
||||||
if(h.is_paused()) continue;
|
if(h.is_paused()) continue;
|
||||||
|
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) != -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if(h.state() == torrent_status::downloading || (h.state() != torrent_status::checking_files && h.state() != torrent_status::queued_for_checking && h.progress() < 1.)) {
|
if(h.state() == torrent_status::downloading || (h.state() != torrent_status::checking_files && h.state() != torrent_status::queued_for_checking && h.progress() < 1.)) {
|
||||||
// What are you doing here? go back to download tab!
|
// What are you doing here? go back to download tab!
|
||||||
qDebug("Info: a torrent was moved from finished to download tab");
|
qDebug("Info: a torrent was moved from finished to download tab");
|
||||||
@@ -218,19 +249,21 @@ void FinishedTorrents::updateFinishedList(){
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(h.state() == torrent_status::checking_files){
|
if(h.state() == torrent_status::checking_files){
|
||||||
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) == -1) {
|
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
|
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
|
||||||
setRowColor(row, QString::fromUtf8("grey"));
|
setRowColor(row, QString::fromUtf8("grey"));
|
||||||
}
|
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)h.progress()));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
setRowColor(row, QString::fromUtf8("orange"));
|
setRowColor(row, QString::fromUtf8("orange"));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png"))), Qt::DecorationRole);
|
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png"))), Qt::DecorationRole);
|
||||||
|
if(!finishedList->isColumnHidden(F_UPSPEED)) {
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)h.upload_payload_rate()));
|
finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)h.upload_payload_rate()));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_SEEDSLEECH), QVariant(misc::toQString(h.num_seeds(), true)+"/"+misc::toQString(h.num_peers() - h.num_seeds(), true)));
|
}
|
||||||
|
if(!finishedList->isColumnHidden(F_LEECH)) {
|
||||||
|
finishedListModel->setData(finishedListModel->index(row, F_LEECH), misc::toQString(h.num_peers() - h.num_seeds(), true));
|
||||||
|
}
|
||||||
|
if(!finishedList->isColumnHidden(F_RATIO)) {
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_RATIO), QVariant(misc::toQString(BTSession->getRealRatio(hash))));
|
finishedListModel->setData(finishedListModel->index(row, F_RATIO), QVariant(misc::toQString(BTSession->getRealRatio(hash))));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)1.));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,10 +280,11 @@ int FinishedTorrents::getRowFromHash(QString hash) const{
|
|||||||
// Note: does not actually pause the torrent in BT Session
|
// Note: does not actually pause the torrent in BT Session
|
||||||
void FinishedTorrents::pauseTorrent(QString hash) {
|
void FinishedTorrents::pauseTorrent(QString hash) {
|
||||||
int row = getRowFromHash(hash);
|
int row = getRowFromHash(hash);
|
||||||
Q_ASSERT(row != -1);
|
if(row == -1)
|
||||||
|
return;
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)0.0));
|
finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)0.0));
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_NAME), QIcon(QString::fromUtf8(":/Icons/skin/paused.png")), Qt::DecorationRole);
|
finishedListModel->setData(finishedListModel->index(row, F_NAME), QIcon(QString::fromUtf8(":/Icons/skin/paused.png")), Qt::DecorationRole);
|
||||||
finishedListModel->setData(finishedListModel->index(row, F_SEEDSLEECH), QVariant(QString::fromUtf8("0/0")));
|
finishedListModel->setData(finishedListModel->index(row, F_LEECH), QVariant(QString::fromUtf8("0")));
|
||||||
setRowColor(row, QString::fromUtf8("red"));
|
setRowColor(row, QString::fromUtf8("red"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,11 +314,14 @@ void FinishedTorrents::deleteTorrent(QString hash){
|
|||||||
|
|
||||||
// Show torrent properties dialog
|
// Show torrent properties dialog
|
||||||
void FinishedTorrents::showProperties(const QModelIndex &index){
|
void FinishedTorrents::showProperties(const QModelIndex &index){
|
||||||
int row = index.row();
|
showPropertiesFromHash(finishedListModel->data(finishedListModel->index(index.row(), F_HASH)).toString());
|
||||||
QString hash = finishedListModel->data(finishedListModel->index(row, F_HASH)).toString();
|
}
|
||||||
|
|
||||||
|
void FinishedTorrents::showPropertiesFromHash(QString hash){
|
||||||
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
properties *prop = new properties(this, BTSession, h);
|
properties *prop = new properties(this, BTSession, h);
|
||||||
connect(prop, SIGNAL(filteredFilesChanged(QString)), this, SLOT(updateFileSize(QString)));
|
connect(prop, SIGNAL(filteredFilesChanged(QString)), this, SLOT(updateFileSize(QString)));
|
||||||
|
connect(prop, SIGNAL(trackersChanged(QString)), BTSession, SLOT(saveTrackerFile(QString)));
|
||||||
prop->show();
|
prop->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,8 +347,6 @@ void FinishedTorrents::displayFinishedListMenu(const QPoint& pos){
|
|||||||
QModelIndex index;
|
QModelIndex index;
|
||||||
// Enable/disable pause/start action given the DL state
|
// Enable/disable pause/start action given the DL state
|
||||||
QModelIndexList selectedIndexes = finishedList->selectionModel()->selectedIndexes();
|
QModelIndexList selectedIndexes = finishedList->selectionModel()->selectedIndexes();
|
||||||
QSettings settings("qBittorrent", "qBittorrent");
|
|
||||||
QString previewProgram = settings.value("Options/Misc/PreviewProgram", QString()).toString();
|
|
||||||
bool has_pause = false, has_start = false, has_preview = false;
|
bool has_pause = false, has_start = false, has_preview = false;
|
||||||
foreach(index, selectedIndexes) {
|
foreach(index, selectedIndexes) {
|
||||||
if(index.column() == F_NAME) {
|
if(index.column() == F_NAME) {
|
||||||
@@ -331,7 +366,7 @@ void FinishedTorrents::displayFinishedListMenu(const QPoint& pos){
|
|||||||
has_pause = true;
|
has_pause = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!previewProgram.isEmpty() && BTSession->isFilePreviewPossible(hash) && !has_preview) {
|
if(BTSession->isFilePreviewPossible(hash) && !has_preview) {
|
||||||
myFinishedListMenu.addAction(actionPreview_file);
|
myFinishedListMenu.addAction(actionPreview_file);
|
||||||
has_preview = true;
|
has_preview = true;
|
||||||
}
|
}
|
||||||
@@ -344,12 +379,150 @@ void FinishedTorrents::displayFinishedListMenu(const QPoint& pos){
|
|||||||
myFinishedListMenu.addSeparator();
|
myFinishedListMenu.addSeparator();
|
||||||
myFinishedListMenu.addAction(actionSet_upload_limit);
|
myFinishedListMenu.addAction(actionSet_upload_limit);
|
||||||
myFinishedListMenu.addSeparator();
|
myFinishedListMenu.addSeparator();
|
||||||
|
myFinishedListMenu.addAction(actionOpen_destination_folder);
|
||||||
myFinishedListMenu.addAction(actionTorrent_Properties);
|
myFinishedListMenu.addAction(actionTorrent_Properties);
|
||||||
|
myFinishedListMenu.addSeparator();
|
||||||
|
myFinishedListMenu.addAction(actionBuy_it);
|
||||||
|
|
||||||
// Call menu
|
// Call menu
|
||||||
// XXX: why mapToGlobal() is not enough?
|
// XXX: why mapToGlobal() is not enough?
|
||||||
myFinishedListMenu.exec(mapToGlobal(pos)+QPoint(10,55));
|
myFinishedListMenu.exec(mapToGlobal(pos)+QPoint(10,55));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hiding Columns functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// hide/show columns menu
|
||||||
|
void FinishedTorrents::displayFinishedHoSMenu(const QPoint& pos){
|
||||||
|
QMenu hideshowColumn(this);
|
||||||
|
hideshowColumn.setTitle(tr("Hide or Show Column"));
|
||||||
|
for(int i=0; i<=F_RATIO; i++) {
|
||||||
|
hideshowColumn.addAction(getActionHoSCol(i));
|
||||||
|
}
|
||||||
|
// Call menu
|
||||||
|
hideshowColumn.exec(mapToGlobal(pos)+QPoint(10,55));
|
||||||
|
}
|
||||||
|
|
||||||
|
// toggle hide/show a column
|
||||||
|
void FinishedTorrents::hideOrShowColumn(int index) {
|
||||||
|
unsigned int nbVisibleColumns = 0;
|
||||||
|
unsigned int nbCols = finishedListModel->columnCount();
|
||||||
|
// Count visible columns
|
||||||
|
for(unsigned int i=0; i<nbCols; ++i) {
|
||||||
|
if(!finishedList->isColumnHidden(i))
|
||||||
|
++nbVisibleColumns;
|
||||||
|
}
|
||||||
|
if(!finishedList->isColumnHidden(index)) {
|
||||||
|
// User wants to hide the column
|
||||||
|
// Is there at least one other visible column?
|
||||||
|
if(nbVisibleColumns <= 1) return;
|
||||||
|
// User can hide the column, do it.
|
||||||
|
finishedList->setColumnHidden(index, true);
|
||||||
|
getActionHoSCol(index)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
|
||||||
|
--nbVisibleColumns;
|
||||||
|
} else {
|
||||||
|
// User want to display the column
|
||||||
|
finishedList->setColumnHidden(index, false);
|
||||||
|
getActionHoSCol(index)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
|
||||||
|
++nbVisibleColumns;
|
||||||
|
}
|
||||||
|
//resize all others non-hidden columns
|
||||||
|
for(unsigned int i=0; i<nbCols; ++i) {
|
||||||
|
if(!finishedList->isColumnHidden(i)) {
|
||||||
|
finishedList->setColumnWidth(i, (int)ceil(finishedList->columnWidth(i)+(finishedList->columnWidth(index)/nbVisibleColumns)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinishedTorrents::hideOrShowColumnName() {
|
||||||
|
hideOrShowColumn(F_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinishedTorrents::hideOrShowColumnSize() {
|
||||||
|
hideOrShowColumn(F_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinishedTorrents::hideOrShowColumnUpSpeed() {
|
||||||
|
hideOrShowColumn(F_UPSPEED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinishedTorrents::hideOrShowColumnLeechers() {
|
||||||
|
hideOrShowColumn(F_LEECH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinishedTorrents::hideOrShowColumnRatio() {
|
||||||
|
hideOrShowColumn(F_RATIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the previous settings, and hide the columns
|
||||||
|
bool FinishedTorrents::loadHiddenColumns() {
|
||||||
|
bool loaded = false;
|
||||||
|
QSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
QString line = settings.value("FinishedListColsHoS", QString()).toString();
|
||||||
|
QStringList ishidden_list;
|
||||||
|
if(!line.isEmpty()) {
|
||||||
|
ishidden_list = line.split(' ');
|
||||||
|
if(ishidden_list.size() == finishedListModel->columnCount()-1) {
|
||||||
|
unsigned int listSize = ishidden_list.size();
|
||||||
|
for(unsigned int i=0; i<listSize; ++i){
|
||||||
|
finishedList->header()->resizeSection(i, ishidden_list.at(i).toInt());
|
||||||
|
}
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(int i=0; i<finishedListModel->columnCount()-1; i++) {
|
||||||
|
if(loaded && ishidden_list.at(i) == "0") {
|
||||||
|
finishedList->setColumnHidden(i, true);
|
||||||
|
getActionHoSCol(i)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
|
||||||
|
} else {
|
||||||
|
getActionHoSCol(i)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the hidden columns in settings
|
||||||
|
void FinishedTorrents::saveHiddenColumns() {
|
||||||
|
QSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
QStringList ishidden_list;
|
||||||
|
short nbColumns = finishedListModel->columnCount()-1;
|
||||||
|
|
||||||
|
for(short i=0; i<nbColumns; ++i){
|
||||||
|
if(finishedList->isColumnHidden(i)) {
|
||||||
|
ishidden_list << QString::fromUtf8(misc::toString(0).c_str());
|
||||||
|
} else {
|
||||||
|
ishidden_list << QString::fromUtf8(misc::toString(1).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.setValue("FinishedListColsHoS", ishidden_list.join(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
// getter, return the action hide or show whose id is index
|
||||||
|
QAction* FinishedTorrents::getActionHoSCol(int index) {
|
||||||
|
switch(index) {
|
||||||
|
case F_NAME :
|
||||||
|
return actionHOSColName;
|
||||||
|
break;
|
||||||
|
case F_SIZE :
|
||||||
|
return actionHOSColSize;
|
||||||
|
break;
|
||||||
|
case F_UPSPEED :
|
||||||
|
return actionHOSColUpSpeed;
|
||||||
|
break;
|
||||||
|
case F_LEECH :
|
||||||
|
return actionHOSColLeechers;
|
||||||
|
break;
|
||||||
|
case F_RATIO :
|
||||||
|
return actionHOSColRatio;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sorting functions
|
* Sorting functions
|
||||||
*/
|
*/
|
||||||
@@ -367,7 +540,6 @@ void FinishedTorrents::sortFinishedList(int index){
|
|||||||
switch(index){
|
switch(index){
|
||||||
case F_SIZE:
|
case F_SIZE:
|
||||||
case F_UPSPEED:
|
case F_UPSPEED:
|
||||||
case F_PROGRESS:
|
|
||||||
sortFinishedListFloat(index, sortOrder);
|
sortFinishedListFloat(index, sortOrder);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ class FinishedTorrents : public QWidget, public Ui::seeding {
|
|||||||
FinishedListDelegate *finishedListDelegate;
|
FinishedListDelegate *finishedListDelegate;
|
||||||
QStandardItemModel *finishedListModel;
|
QStandardItemModel *finishedListModel;
|
||||||
unsigned int nbFinished;
|
unsigned int nbFinished;
|
||||||
|
void hideOrShowColumn(int index);
|
||||||
|
bool loadHiddenColumns();
|
||||||
|
void saveHiddenColumns();
|
||||||
|
QAction* getActionHoSCol(int index);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FinishedTorrents(QObject *parent, bittorrent *BTSession);
|
FinishedTorrents(QObject *parent, bittorrent *BTSession);
|
||||||
@@ -53,6 +57,7 @@ class FinishedTorrents : public QWidget, public Ui::seeding {
|
|||||||
protected slots:
|
protected slots:
|
||||||
void showProperties(const QModelIndex &index);
|
void showProperties(const QModelIndex &index);
|
||||||
void displayFinishedListMenu(const QPoint&);
|
void displayFinishedListMenu(const QPoint&);
|
||||||
|
void displayFinishedHoSMenu(const QPoint&);
|
||||||
void setRowColor(int row, QString color);
|
void setRowColor(int row, QString color);
|
||||||
void saveColWidthFinishedList() const;
|
void saveColWidthFinishedList() const;
|
||||||
void sortFinishedList(int index);
|
void sortFinishedList(int index);
|
||||||
@@ -62,6 +67,11 @@ class FinishedTorrents : public QWidget, public Ui::seeding {
|
|||||||
void torrentAdded(QString path, QTorrentHandle& h, bool fastResume);
|
void torrentAdded(QString path, QTorrentHandle& h, bool fastResume);
|
||||||
void on_actionSet_upload_limit_triggered();
|
void on_actionSet_upload_limit_triggered();
|
||||||
void notifyTorrentDoubleClicked(const QModelIndex& index);
|
void notifyTorrentDoubleClicked(const QModelIndex& index);
|
||||||
|
void hideOrShowColumnName();
|
||||||
|
void hideOrShowColumnSize();
|
||||||
|
void hideOrShowColumnUpSpeed();
|
||||||
|
void hideOrShowColumnLeechers();
|
||||||
|
void hideOrShowColumnRatio();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void addTorrent(QString hash);
|
void addTorrent(QString hash);
|
||||||
@@ -70,10 +80,11 @@ class FinishedTorrents : public QWidget, public Ui::seeding {
|
|||||||
void resumeTorrent(QString hash);
|
void resumeTorrent(QString hash);
|
||||||
void propertiesSelection();
|
void propertiesSelection();
|
||||||
void deleteTorrent(QString hash);
|
void deleteTorrent(QString hash);
|
||||||
|
void showPropertiesFromHash(QString hash);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void torrentMovedFromFinishedList(QString);
|
void torrentMovedFromFinishedList(QString);
|
||||||
void torrentDoubleClicked(QString hash);
|
void torrentDoubleClicked(QString hash, bool finished);
|
||||||
void finishedTorrentsNumberChanged(unsigned int);
|
void finishedTorrentsNumberChanged(unsigned int);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
667
src/GUI.cpp
21
src/GUI.h
@@ -46,6 +46,7 @@ class options_imp;
|
|||||||
class QTabWidget;
|
class QTabWidget;
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
|
class HttpServer;
|
||||||
|
|
||||||
class GUI : public QMainWindow, private Ui::MainWindow{
|
class GUI : public QMainWindow, private Ui::MainWindow{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -59,12 +60,15 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
QTabWidget *tabs;
|
QTabWidget *tabs;
|
||||||
options_imp *options;
|
options_imp *options;
|
||||||
QSystemTrayIcon *myTrayIcon;
|
QSystemTrayIcon *myTrayIcon;
|
||||||
|
QTimer *systrayCreator;
|
||||||
QMenu *myTrayIconMenu;
|
QMenu *myTrayIconMenu;
|
||||||
DownloadingTorrents *downloadingTorrentTab;
|
DownloadingTorrents *downloadingTorrentTab;
|
||||||
FinishedTorrents *finishedTorrentTab;
|
FinishedTorrents *finishedTorrentTab;
|
||||||
QLabel *connecStatusLblIcon;
|
QLabel *connecStatusLblIcon;
|
||||||
bool systrayIntegration;
|
bool systrayIntegration;
|
||||||
|
bool displaySpeedInTitle;
|
||||||
bool force_exit;
|
bool force_exit;
|
||||||
|
unsigned int refreshInterval;
|
||||||
QTimer *refresher;
|
QTimer *refresher;
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
QShortcut *switchSearchShortcut;
|
QShortcut *switchSearchShortcut;
|
||||||
@@ -72,12 +76,12 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
QShortcut *switchDownShortcut;
|
QShortcut *switchDownShortcut;
|
||||||
QShortcut *switchUpShortcut;
|
QShortcut *switchUpShortcut;
|
||||||
QShortcut *switchRSSShortcut;
|
QShortcut *switchRSSShortcut;
|
||||||
// Preview
|
|
||||||
QProcess *previewProcess;
|
|
||||||
// Search
|
// Search
|
||||||
SearchEngine *searchEngine;
|
SearchEngine *searchEngine;
|
||||||
// RSS
|
// RSS
|
||||||
RSSImp *rssWidget;
|
RSSImp *rssWidget;
|
||||||
|
// Web UI
|
||||||
|
HttpServer *httpServer;
|
||||||
// Misc
|
// Misc
|
||||||
QTcpServer *tcpServer;
|
QTcpServer *tcpServer;
|
||||||
QTcpSocket *clientConnection;
|
QTcpSocket *clientConnection;
|
||||||
@@ -94,9 +98,9 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
void readParamsOnSocket();
|
void readParamsOnSocket();
|
||||||
void acceptConnection();
|
void acceptConnection();
|
||||||
void togglePausedState(QString hash);
|
void togglePausedState(QString hash);
|
||||||
|
void torrentDoubleClicked(QString hash, bool finished);
|
||||||
void on_actionPreview_file_triggered();
|
void on_actionPreview_file_triggered();
|
||||||
void previewFile(QString filePath);
|
void previewFile(QString filePath);
|
||||||
void cleanTempPreviewFile(int, QProcess::ExitStatus) const;
|
|
||||||
void balloonClicked();
|
void balloonClicked();
|
||||||
void writeSettings();
|
void writeSettings();
|
||||||
void readSettings();
|
void readSettings();
|
||||||
@@ -106,6 +110,7 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
void updateFinishedTorrentNumber(unsigned int nb);
|
void updateFinishedTorrentNumber(unsigned int nb);
|
||||||
void fullDiskError(QTorrentHandle& h) const;
|
void fullDiskError(QTorrentHandle& h) const;
|
||||||
void handleDownloadFromUrlFailure(QString, QString) const;
|
void handleDownloadFromUrlFailure(QString, QString) const;
|
||||||
|
void createSystrayDelayed();
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
void createKeyboardShortcuts();
|
void createKeyboardShortcuts();
|
||||||
void displayDownTab() const;
|
void displayDownTab() const;
|
||||||
@@ -127,13 +132,18 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
void checkConnectionStatus();
|
void checkConnectionStatus();
|
||||||
void configureSession(bool deleteOptions);
|
void configureSession(bool deleteOptions);
|
||||||
void processParams(const QStringList& params);
|
void processParams(const QStringList& params);
|
||||||
|
void addTorrent(QString path);
|
||||||
void addUnauthenticatedTracker(QPair<QTorrentHandle,QString> tracker);
|
void addUnauthenticatedTracker(QPair<QTorrentHandle,QString> tracker);
|
||||||
void processScannedFiles(const QStringList& params);
|
void processScannedFiles(const QStringList& params);
|
||||||
void processDownloadedFiles(QString path, QString url);
|
void processDownloadedFiles(QString path, QString url);
|
||||||
void downloadFromURLList(const QStringList& urls);
|
void downloadFromURLList(const QStringList& urls);
|
||||||
|
void deleteTorrent(QString hash);
|
||||||
|
void deleteRatioTorrent(QString fileName);
|
||||||
void finishedTorrent(QTorrentHandle& h) const;
|
void finishedTorrent(QTorrentHandle& h) const;
|
||||||
void torrentChecked(QString hash) const;
|
void torrentChecked(QString hash) const;
|
||||||
void updateLists();
|
void updateLists();
|
||||||
|
bool initWebUi(QString username, QString password, int port);
|
||||||
|
void pauseTorrent(QString hash);
|
||||||
// Options slots
|
// Options slots
|
||||||
void on_actionOptions_triggered();
|
void on_actionOptions_triggered();
|
||||||
void OptionsSaved(QString info, bool deleteOptions);
|
void OptionsSaved(QString info, bool deleteOptions);
|
||||||
@@ -144,6 +154,9 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
public slots:
|
public slots:
|
||||||
void trackerAuthenticationRequired(QTorrentHandle& h);
|
void trackerAuthenticationRequired(QTorrentHandle& h);
|
||||||
void setTabText(int index, QString text) const;
|
void setTabText(int index, QString text) const;
|
||||||
|
void openDestinationFolder() const;
|
||||||
|
void goBuyPage() const;
|
||||||
|
void updateRatio();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent *);
|
void closeEvent(QCloseEvent *);
|
||||||
@@ -154,7 +167,7 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
GUI(QWidget *parent=0, QStringList torrentCmdLine=QStringList());
|
GUI(QWidget *parent=0, QStringList torrentCmdLine=QStringList());
|
||||||
~GUI();
|
~GUI();
|
||||||
// Methods
|
// Methods
|
||||||
unsigned int getCurrentTabIndex() const;
|
int getCurrentTabIndex() const;
|
||||||
QPoint screenCenter() const;
|
QPoint screenCenter() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
BIN
src/Icons/bt_settings.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/Icons/configure.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 820 B |
BIN
src/Icons/download.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/Icons/edit_clear.png
Normal file
|
After Width: | Height: | Size: 575 B |
|
Before Width: | Height: | Size: 1.4 KiB |
BIN
src/Icons/file.png
Normal file
|
After Width: | Height: | Size: 704 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 998 B |
|
Before Width: | Height: | Size: 558 B |
BIN
src/Icons/folder.png
Normal file
|
After Width: | Height: | Size: 449 B |
BIN
src/Icons/gear.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/Icons/money.png
Normal file
|
After Width: | Height: | Size: 813 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 532 B |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.2 KiB |
@@ -1,22 +1,23 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Categories=Qt;Application;Network;P2P
|
Categories=Qt;Application;Network;P2P
|
||||||
Comment=V1.0.0
|
Comment=V1.1.0
|
||||||
Encoding=UTF-8
|
|
||||||
Exec=qbittorrent
|
Exec=qbittorrent
|
||||||
GenericName=Bittorrent client
|
GenericName=Bittorrent client
|
||||||
GenericName[fr]=Client Bittorrent
|
GenericName[bg]=Торент клиент
|
||||||
GenericName[nl]=Bittorrent client
|
GenericName[de]=Bittorren Client
|
||||||
|
GenericName[el]=Τορεντ πελάτης
|
||||||
GenericName[es]=Cliente Bittorrent
|
GenericName[es]=Cliente Bittorrent
|
||||||
|
GenericName[fr]=Client Bittorrent
|
||||||
|
GenericName[it]=Client Bittorrent
|
||||||
|
GenericName[ja]=Bittorrent クライアント
|
||||||
|
GenericName[ko]=비토렌트 클라이언트
|
||||||
|
GenericName[nl]=Bittorrent client
|
||||||
|
GenericName[pl]=Klient Bittorrent
|
||||||
|
GenericName[ru]=клиент Bittorrent
|
||||||
GenericName[sv]=Bittorrent-klient
|
GenericName[sv]=Bittorrent-klient
|
||||||
GenericName[tr]=Bittorrent istemcisi
|
GenericName[tr]=Bittorrent istemcisi
|
||||||
GenericName[de]=Bittorren Client
|
|
||||||
GenericName[pl]=Klient Bittorrent
|
|
||||||
GenericName[zh]=Bittorrent之用户
|
|
||||||
GenericName[ko]=비토렌트 클라이언트
|
|
||||||
GenericName[el]=Τορεντ πελάτης
|
|
||||||
GenericName[bg]=Торент клиент
|
|
||||||
GenericName[uk]=Bittorrent-клієнт
|
GenericName[uk]=Bittorrent-клієнт
|
||||||
GenericName[ru]=клиент Bittorrent
|
GenericName[zh]=Bittorrent之用户
|
||||||
Icon=qbittorrent
|
Icon=qbittorrent
|
||||||
MimeType=application/x-bittorrent
|
MimeType=application/x-bittorrent
|
||||||
Name=qBittorrent
|
Name=qBittorrent
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 948 B |
|
Before Width: | Height: | Size: 2.2 KiB |
BIN
src/Icons/rss16.png
Normal file
|
After Width: | Height: | Size: 607 B |
BIN
src/Icons/rss32.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 75 KiB |
BIN
src/Icons/star.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/Icons/subscribe.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/Icons/subscribe16.png
Normal file
|
After Width: | Height: | Size: 739 B |
|
Before Width: | Height: | Size: 513 B After Width: | Height: | Size: 856 B |
BIN
src/Icons/unsubscribe.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/Icons/unsubscribe16.png
Normal file
|
After Width: | Height: | Size: 765 B |
@@ -16,13 +16,194 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget" >
|
<widget class="QWidget" name="centralwidget" >
|
||||||
<layout class="QVBoxLayout" >
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>58</y>
|
||||||
|
<width>849</width>
|
||||||
|
<height>505</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout" >
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="_3" >
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="margin" >
|
<property name="margin" >
|
||||||
<number>9</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0" >
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_DLSpeed_2" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Total DL Speed:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLCDNumber" name="LCD_DownSpeed" >
|
||||||
|
<property name="autoFillBackground" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow" >
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<property name="smallDecimalPoint" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="numDigits" >
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="segmentStyle" >
|
||||||
|
<enum>QLCDNumber::Flat</enum>
|
||||||
|
</property>
|
||||||
|
<property name="value" stdset="0" >
|
||||||
|
<double>0.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="intValue" stdset="0" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="unitDL_2" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>KiB/s</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0" >
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Session ratio: </string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLCDNumber" name="LCD_Ratio" >
|
||||||
|
<property name="autoFillBackground" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="numDigits" >
|
||||||
|
<number>4</number>
|
||||||
|
</property>
|
||||||
|
<property name="segmentStyle" >
|
||||||
|
<enum>QLCDNumber::Flat</enum>
|
||||||
|
</property>
|
||||||
|
<property name="value" stdset="0" >
|
||||||
|
<double>1.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_ratio_icon" >
|
||||||
|
<property name="text" >
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0" >
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_UPSpeed_2" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Total UP Speed:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLCDNumber" name="LCD_UpSpeed" >
|
||||||
|
<property name="autoFillBackground" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="smallDecimalPoint" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="numDigits" >
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="segmentStyle" >
|
||||||
|
<enum>QLCDNumber::Flat</enum>
|
||||||
|
</property>
|
||||||
|
<property name="value" stdset="0" >
|
||||||
|
<double>0.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="unitUP_2" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>KiB/s</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment" >
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0" >
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenuBar" name="menubar" >
|
<widget class="QMenuBar" name="menubar" >
|
||||||
@@ -31,7 +212,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>849</width>
|
<width>849</width>
|
||||||
<height>29</height>
|
<height>26</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menu_Edit" >
|
<widget class="QMenu" name="menu_Edit" >
|
||||||
@@ -82,6 +263,14 @@
|
|||||||
<property name="enabled" >
|
<property name="enabled" >
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>26</y>
|
||||||
|
<width>849</width>
|
||||||
|
<height>32</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
<property name="contextMenuPolicy" >
|
<property name="contextMenuPolicy" >
|
||||||
<enum>Qt::NoContextMenu</enum>
|
<enum>Qt::NoContextMenu</enum>
|
||||||
</property>
|
</property>
|
||||||
@@ -98,7 +287,7 @@
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="toolBarArea" >
|
<attribute name="toolBarArea" >
|
||||||
<number>4</number>
|
<enum>TopToolBarArea</enum>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="toolBarBreak" >
|
<attribute name="toolBarBreak" >
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
|||||||
@@ -61,10 +61,8 @@ class PreviewListDelegate: public QItemDelegate {
|
|||||||
newopt.maximum = 100;
|
newopt.maximum = 100;
|
||||||
newopt.minimum = 0;
|
newopt.minimum = 0;
|
||||||
newopt.state |= QStyle::State_Enabled;
|
newopt.state |= QStyle::State_Enabled;
|
||||||
newopt.textVisible = false;
|
newopt.textVisible = true;
|
||||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
|
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
|
||||||
painter->setPen(QColor("Black"));
|
|
||||||
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#define SIZE 1
|
#define SIZE 1
|
||||||
#define PROGRESS 2
|
#define PROGRESS 2
|
||||||
#define PRIORITY 3
|
#define PRIORITY 3
|
||||||
|
#define INDEX 4
|
||||||
|
|
||||||
#define IGNORED 0
|
#define IGNORED 0
|
||||||
#define NORMAL 1
|
#define NORMAL 1
|
||||||
@@ -74,10 +75,8 @@ class PropListDelegate: public QItemDelegate {
|
|||||||
newopt.maximum = 100;
|
newopt.maximum = 100;
|
||||||
newopt.minimum = 0;
|
newopt.minimum = 0;
|
||||||
newopt.state |= QStyle::State_Enabled;
|
newopt.state |= QStyle::State_Enabled;
|
||||||
newopt.textVisible = false;
|
newopt.textVisible = true;
|
||||||
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
|
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
|
||||||
painter->setPen(QColor("Black"));
|
|
||||||
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PRIORITY:{
|
case PRIORITY:{
|
||||||
@@ -115,7 +114,6 @@ class PropListDelegate: public QItemDelegate {
|
|||||||
|
|
||||||
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const {
|
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const {
|
||||||
if(index.column() != PRIORITY) return 0;
|
if(index.column() != PRIORITY) return 0;
|
||||||
if(onlyOneItem(index)) return 0;
|
|
||||||
QComboBox* editor = new QComboBox(parent);
|
QComboBox* editor = new QComboBox(parent);
|
||||||
editor->setFocusPolicy(Qt::StrongFocus);
|
editor->setFocusPolicy(Qt::StrongFocus);
|
||||||
editor->addItem(tr("Ignored"));
|
editor->addItem(tr("Ignored"));
|
||||||
@@ -128,6 +126,7 @@ class PropListDelegate: public QItemDelegate {
|
|||||||
void setEditorData(QWidget *editor, const QModelIndex &index) const {
|
void setEditorData(QWidget *editor, const QModelIndex &index) const {
|
||||||
unsigned short val = index.model()->data(index, Qt::DisplayRole).toInt();
|
unsigned short val = index.model()->data(index, Qt::DisplayRole).toInt();
|
||||||
QComboBox *combobox = static_cast<QComboBox*>(editor);
|
QComboBox *combobox = static_cast<QComboBox*>(editor);
|
||||||
|
qDebug("Set Editor data: Prio is %d", val);
|
||||||
switch(val){
|
switch(val){
|
||||||
case IGNORED:
|
case IGNORED:
|
||||||
combobox->setCurrentIndex(0);
|
combobox->setCurrentIndex(0);
|
||||||
@@ -156,28 +155,11 @@ class PropListDelegate: public QItemDelegate {
|
|||||||
return textRect.size();
|
return textRect.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool onlyOneItem(const QModelIndex& index) const {
|
|
||||||
const QAbstractItemModel *model = index.model();
|
|
||||||
unsigned int nbRows = model->rowCount();
|
|
||||||
if(nbRows == 1) return true;
|
|
||||||
for(unsigned int i=0; i<nbRows; ++i){
|
|
||||||
if((unsigned int)index.row() == i) continue;
|
|
||||||
if(model->data(model->index(i, PRIORITY)).toInt()) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
|
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
|
||||||
QComboBox *combobox = static_cast<QComboBox*>(editor);
|
QComboBox *combobox = static_cast<QComboBox*>(editor);
|
||||||
int value = combobox->currentIndex();
|
int value = combobox->currentIndex();
|
||||||
qDebug("Setting combobox value in index: %d", value);
|
qDebug("Setting combobox value in index: %d", value);
|
||||||
QString color;
|
|
||||||
if(value) {
|
|
||||||
color = QString::fromUtf8("green");
|
|
||||||
} else {
|
|
||||||
color = QString::fromUtf8("red");
|
|
||||||
}
|
|
||||||
unsigned short old_val = index.model()->data(index, Qt::DisplayRole).toInt();
|
unsigned short old_val = index.model()->data(index, Qt::DisplayRole).toInt();
|
||||||
switch(value){
|
switch(value){
|
||||||
case 0:
|
case 0:
|
||||||
@@ -185,20 +167,32 @@ class PropListDelegate: public QItemDelegate {
|
|||||||
model->setData(index, QVariant(IGNORED));
|
model->setData(index, QVariant(IGNORED));
|
||||||
if(filteredFilesChanged != 0)
|
if(filteredFilesChanged != 0)
|
||||||
*filteredFilesChanged = true;
|
*filteredFilesChanged = true;
|
||||||
|
} else {
|
||||||
|
// XXX: hack to force the model to send the itemChanged() signal
|
||||||
|
model->setData(index, QVariant(NORMAL));
|
||||||
|
model->setData(index, QVariant(IGNORED));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if(old_val != NORMAL){
|
// if(old_val != NORMAL){
|
||||||
|
// model->setData(index, QVariant(NORMAL));
|
||||||
|
// if(filteredFilesChanged != 0)
|
||||||
|
// *filteredFilesChanged = true;
|
||||||
|
// } else {
|
||||||
|
model->setData(index, QVariant(HIGH));
|
||||||
model->setData(index, QVariant(NORMAL));
|
model->setData(index, QVariant(NORMAL));
|
||||||
if(filteredFilesChanged != 0)
|
if(filteredFilesChanged != 0)
|
||||||
*filteredFilesChanged = true;
|
*filteredFilesChanged = true;
|
||||||
}
|
// }
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if(old_val != HIGH){
|
if(old_val != HIGH){
|
||||||
model->setData(index, QVariant(HIGH));
|
model->setData(index, QVariant(HIGH));
|
||||||
if(filteredFilesChanged != 0)
|
if(filteredFilesChanged != 0)
|
||||||
*filteredFilesChanged = true;
|
*filteredFilesChanged = true;
|
||||||
|
} else {
|
||||||
|
model->setData(index, QVariant(NORMAL));
|
||||||
|
model->setData(index, QVariant(HIGH));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
@@ -206,6 +200,9 @@ class PropListDelegate: public QItemDelegate {
|
|||||||
model->setData(index, QVariant(MAXIMUM));
|
model->setData(index, QVariant(MAXIMUM));
|
||||||
if(filteredFilesChanged != 0)
|
if(filteredFilesChanged != 0)
|
||||||
*filteredFilesChanged = true;
|
*filteredFilesChanged = true;
|
||||||
|
} else {
|
||||||
|
model->setData(index, QVariant(HIGH));
|
||||||
|
model->setData(index, QVariant(MAXIMUM));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -213,16 +210,17 @@ class PropListDelegate: public QItemDelegate {
|
|||||||
model->setData(index, QVariant(NORMAL));
|
model->setData(index, QVariant(NORMAL));
|
||||||
if(filteredFilesChanged != 0)
|
if(filteredFilesChanged != 0)
|
||||||
*filteredFilesChanged = true;
|
*filteredFilesChanged = true;
|
||||||
|
} else {
|
||||||
|
model->setData(index, QVariant(HIGH));
|
||||||
|
model->setData(index, QVariant(NORMAL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(int i=0; i<model->columnCount(); ++i){
|
|
||||||
model->setData(model->index(index.row(), i), QVariant(QColor(color)), Qt::ForegroundRole);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const {
|
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const {
|
||||||
editor->setGeometry(option.rect);
|
editor->setGeometry(option.rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
53
src/TrackersAdditionDlg.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TRACKERSADDITION_H
|
||||||
|
#define TRACKERSADDITION_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QStringList>
|
||||||
|
#include "ui_trackersAdd.h"
|
||||||
|
|
||||||
|
class TrackersAddDlg : public QDialog, private Ui::TrackersAdditionDlg{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
TrackersAddDlg(QWidget *parent): QDialog(parent){
|
||||||
|
setupUi(this);
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
~TrackersAddDlg(){}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void TrackersToAdd(QStringList trackers);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void on_buttonBox_accepted() {
|
||||||
|
QStringList trackers = trackers_list->toPlainText().trimmed().split("\n");
|
||||||
|
if(trackers.size()) {
|
||||||
|
emit TrackersToAdd(trackers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -44,9 +44,9 @@ class about : public QDialog, private Ui::AboutDlg{
|
|||||||
// Thanks
|
// Thanks
|
||||||
te_thanks->append(QString::fromUtf8("<a name='top'></a>"));
|
te_thanks->append(QString::fromUtf8("<a name='top'></a>"));
|
||||||
te_thanks->append(QString::fromUtf8("<ul><li>I would like to thank sourceforge.net for hosting qBittorrent project.</li>"));
|
te_thanks->append(QString::fromUtf8("<ul><li>I would like to thank sourceforge.net for hosting qBittorrent project.</li>"));
|
||||||
te_thanks->append(QString::fromUtf8("<li>I am happy that Arnaud Demaizière joined the project as a programmer. His help is greatly appreciated</li>"));
|
te_thanks->append(QString::fromUtf8("<li>I am happy that Arnaud Demaizière is contributing to the project as a developer. His help is greatly appreciated</li>"));
|
||||||
te_thanks->append(QString::fromUtf8("<li>I also want to thank Jeffery Fernandez (jeffery@qbittorrent.org), project consultant, webdevelopper and RPM packager, for his help.</li>"));
|
te_thanks->append(QString::fromUtf8("<li>I also want to thank Jeffery Fernandez (jeffery@qbittorrent.org), project consultant, RPM packager, for his help and support.</li>"));
|
||||||
te_thanks->append(QString::fromUtf8("<li>I am gratefull to Peter Koeleman (peter@qbittorrent.org) who is helping port qBittorrent to Windows.</li>"));
|
te_thanks->append(QString::fromUtf8("<li>I am grateful to Peter Koeleman (peter@qbittorrent.org) who is helping port qBittorrent to Windows.</li>"));
|
||||||
te_thanks->append(QString::fromUtf8("<li>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</li></ul><br><br>"));
|
te_thanks->append(QString::fromUtf8("<li>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</li></ul><br><br>"));
|
||||||
te_thanks->scrollToAnchor(QString::fromUtf8("top"));
|
te_thanks->scrollToAnchor(QString::fromUtf8("top"));
|
||||||
// Translation
|
// Translation
|
||||||
|
|||||||
@@ -5,20 +5,29 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>440</width>
|
<width>511</width>
|
||||||
<height>389</height>
|
<height>441</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle" >
|
<property name="windowTitle" >
|
||||||
<string>Torrent addition dialog</string>
|
<string>Torrent addition dialog</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" >
|
<layout class="QVBoxLayout" >
|
||||||
<property name="margin" >
|
|
||||||
<number>9</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="leftMargin" >
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin" >
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin" >
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin" >
|
||||||
|
<number>9</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="fileNameLbl" >
|
<widget class="QLabel" name="fileNameLbl" >
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
@@ -48,12 +57,21 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" >
|
<layout class="QHBoxLayout" >
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="leftMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="savePathTxt" />
|
<widget class="QLineEdit" name="savePathTxt" />
|
||||||
</item>
|
</item>
|
||||||
@@ -108,12 +126,21 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" >
|
<layout class="QHBoxLayout" >
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="leftMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<spacer>
|
<spacer>
|
||||||
<property name="orientation" >
|
<property name="orientation" >
|
||||||
|
|||||||
@@ -157,13 +157,13 @@ class BandwidthAllocationDialog : public QDialog, private Ui_bandwidth_dlg {
|
|||||||
s->set_upload_rate_limit(-1);
|
s->set_upload_rate_limit(-1);
|
||||||
else
|
else
|
||||||
s->set_upload_rate_limit(val*1024);
|
s->set_upload_rate_limit(val*1024);
|
||||||
settings.setValue(QString::fromUtf8("Options/Main/UPLimit"), val);
|
settings.setValue(QString::fromUtf8("Preferences/Connection/GlobalUPLimit"), val);
|
||||||
}else{
|
}else{
|
||||||
if(!val)
|
if(!val)
|
||||||
s->set_download_rate_limit(-1);
|
s->set_download_rate_limit(-1);
|
||||||
else
|
else
|
||||||
s->set_download_rate_limit(val*1024);
|
s->set_download_rate_limit(val*1024);
|
||||||
settings.setValue(QString::fromUtf8("Options/Main/DLLimit"), val);
|
settings.setValue(QString::fromUtf8("Preferences/Connection/GlobalDLLimit"), val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
|
|||||||
264
src/arborescence.h
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARBORESCENCE_H
|
||||||
|
#define ARBORESCENCE_H
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDir>
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
class torrent_file {
|
||||||
|
private:
|
||||||
|
torrent_file *parent;
|
||||||
|
bool is_dir;
|
||||||
|
QString rel_path;
|
||||||
|
QList<torrent_file*> children;
|
||||||
|
size_type size;
|
||||||
|
float progress;
|
||||||
|
int priority;
|
||||||
|
int index; // Index in torrent_info
|
||||||
|
|
||||||
|
public:
|
||||||
|
torrent_file(torrent_file *parent, QString path, bool dir, size_type size=0, int index=-1, float progress=0., int priority=1): parent(parent), is_dir(dir), size(size), progress(progress), priority(priority), index(index){
|
||||||
|
qDebug("created a file with index %d", index);
|
||||||
|
rel_path = QDir::cleanPath(path);
|
||||||
|
if(parent) {
|
||||||
|
parent->updateProgress();
|
||||||
|
parent->updatePriority(priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~torrent_file() {
|
||||||
|
qDeleteAll(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path() const {
|
||||||
|
return rel_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name() const {
|
||||||
|
return rel_path.split(QDir::separator()).last();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateProgress() {
|
||||||
|
Q_ASSERT(is_dir);
|
||||||
|
if(children.isEmpty()) {
|
||||||
|
progress = 0.;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float wanted = 0.;
|
||||||
|
float done = 0.;
|
||||||
|
torrent_file *child;
|
||||||
|
foreach(child, children) {
|
||||||
|
wanted += child->getSize();
|
||||||
|
done += child->getSize()*child->getProgress();
|
||||||
|
}
|
||||||
|
progress = done / wanted;
|
||||||
|
Q_ASSERT(progress >= 0.);
|
||||||
|
Q_ASSERT(progress <= 1.);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePriority(int prio) {
|
||||||
|
Q_ASSERT(is_dir);
|
||||||
|
torrent_file *child;
|
||||||
|
foreach(child, children) {
|
||||||
|
if(child->getPriority() != prio) return;
|
||||||
|
}
|
||||||
|
priority = prio;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPriority() const {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_type getSize() const {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
float getProgress() const {
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getIndex() const {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDir() const {
|
||||||
|
return is_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasChildren() const {
|
||||||
|
return (!children.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<torrent_file*> getChildren() const {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent_file* getChild(QString fileName) const {
|
||||||
|
Q_ASSERT(is_dir);
|
||||||
|
torrent_file* f;
|
||||||
|
foreach(f, children) {
|
||||||
|
if(f->name() == fileName) return f;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addBytes(size_type b) {
|
||||||
|
size += b;
|
||||||
|
if(parent)
|
||||||
|
parent->addBytes(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent_file* addChild(QString fileName, bool dir, size_type size=0, int index = -1, float progress=0., int priority=1) {
|
||||||
|
Q_ASSERT(is_dir);
|
||||||
|
qDebug("Adding a new child of size: %ld", (long)size);
|
||||||
|
torrent_file *f = new torrent_file(this, QDir::cleanPath(rel_path+QDir::separator()+fileName), dir, size, index, progress, priority);
|
||||||
|
children << f;
|
||||||
|
if(size) {
|
||||||
|
addBytes(size);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeFromFS(QString saveDir) {
|
||||||
|
QString full_path = saveDir + QDir::separator() + rel_path;
|
||||||
|
if(!QFile::exists(full_path)) {
|
||||||
|
qDebug("%s does not exist, no need to remove it", full_path.toUtf8().data());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool success = true;
|
||||||
|
torrent_file *f;
|
||||||
|
qDebug("We have %d children", children.size());
|
||||||
|
foreach(f, children) {
|
||||||
|
bool s = f->removeFromFS(saveDir);
|
||||||
|
success = s && success;
|
||||||
|
}
|
||||||
|
if(is_dir) {
|
||||||
|
qDebug("trying to remove directory: %s", full_path.toUtf8().data());
|
||||||
|
QDir dir(full_path);
|
||||||
|
dir.rmdir(full_path);
|
||||||
|
} else {
|
||||||
|
qDebug("trying to remove file: %s", full_path.toUtf8().data());
|
||||||
|
bool s = QFile::remove(full_path);
|
||||||
|
success = s && success;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class arborescence {
|
||||||
|
private:
|
||||||
|
torrent_file *root;
|
||||||
|
|
||||||
|
public:
|
||||||
|
arborescence(torrent_info t) {
|
||||||
|
torrent_info::file_iterator fi = t.begin_files();
|
||||||
|
if(t.num_files() > 1) {
|
||||||
|
root = new torrent_file(0, misc::toQString(t.name()), true);
|
||||||
|
} else {
|
||||||
|
// XXX: Will crash if there is no file in torrent
|
||||||
|
root = new torrent_file(0, misc::toQString(t.name()), false, fi->size, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
while(fi != t.end_files()) {
|
||||||
|
QString path = QDir::cleanPath(misc::toQString(fi->path.string()));
|
||||||
|
addFile(path, fi->size, i);
|
||||||
|
fi++;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
qDebug("real size: %ld, tree size: %ld", (long)t.total_size(), (long)root->getSize());
|
||||||
|
Q_ASSERT(root->getSize() == t.total_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
arborescence(torrent_info t, std::vector<float> fp, int *prioritiesTab) {
|
||||||
|
torrent_info::file_iterator fi = t.begin_files();
|
||||||
|
if(t.num_files() > 1) {
|
||||||
|
qDebug("More than one file in the torrent, setting a folder as root");
|
||||||
|
root = new torrent_file(0, misc::toQString(t.name()), true);
|
||||||
|
} else {
|
||||||
|
// XXX: Will crash if there is no file in torrent
|
||||||
|
qDebug("one file in the torrent, setting it as root with index 0");
|
||||||
|
root = new torrent_file(0, misc::toQString(t.name()), false, fi->size, 0, fp[0], prioritiesTab[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
while(fi != t.end_files()) {
|
||||||
|
QString path = QDir::cleanPath(misc::toQString(fi->path.string()));
|
||||||
|
addFile(path, fi->size, i, fp[i], prioritiesTab[i]);
|
||||||
|
fi++;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
qDebug("real size: %ld, tree size: %ld", (long)t.total_size(), (long)root->getSize());
|
||||||
|
Q_ASSERT(root->getSize() == t.total_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
~arborescence() {
|
||||||
|
delete root;
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent_file* getRoot() const {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeFromFS(QString saveDir) {
|
||||||
|
if(!QFile::exists(saveDir+QDir::separator()+root->path())) return true;
|
||||||
|
bool success = root->removeFromFS(saveDir);
|
||||||
|
QDir root_dir(root->path());
|
||||||
|
root_dir.rmdir(saveDir+QDir::separator()+root->path());
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void addFile(QString path, size_type file_size, int index, float progress=0., int priority=1) {
|
||||||
|
Q_ASSERT(root->isDir());
|
||||||
|
path = QDir::cleanPath(path);
|
||||||
|
Q_ASSERT(path.startsWith(root->path()));
|
||||||
|
QString relative_path = path.remove(0, root->path().size());
|
||||||
|
if(relative_path.at(0) ==QDir::separator())
|
||||||
|
relative_path.remove(0, 1);
|
||||||
|
QStringList fileNames = relative_path.split(QDir::separator());
|
||||||
|
QString fileName;
|
||||||
|
torrent_file *dad = root;
|
||||||
|
unsigned int nb_i = 0;
|
||||||
|
unsigned int size = fileNames.size();
|
||||||
|
foreach(fileName, fileNames) {
|
||||||
|
++nb_i;
|
||||||
|
if(fileName == ".") continue;
|
||||||
|
torrent_file* child = dad->getChild(fileName);
|
||||||
|
if(!child) {
|
||||||
|
if(nb_i != size) {
|
||||||
|
// Folder
|
||||||
|
child = dad->addChild(fileName, true);
|
||||||
|
} else {
|
||||||
|
// File
|
||||||
|
child = dad->addChild(fileName, false, file_size, index, progress, priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dad = child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -23,6 +23,13 @@
|
|||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include "bittorrent.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "downloadThread.h"
|
||||||
|
#include "deleteThread.h"
|
||||||
|
#include "filterParserThread.h"
|
||||||
|
|
||||||
#include <libtorrent/extensions/metadata_transfer.hpp>
|
#include <libtorrent/extensions/metadata_transfer.hpp>
|
||||||
#include <libtorrent/extensions/ut_pex.hpp>
|
#include <libtorrent/extensions/ut_pex.hpp>
|
||||||
@@ -30,59 +37,107 @@
|
|||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
#include <libtorrent/identify_client.hpp>
|
#include <libtorrent/identify_client.hpp>
|
||||||
#include <libtorrent/alert_types.hpp>
|
#include <libtorrent/alert_types.hpp>
|
||||||
#include <libtorrent/ip_filter.hpp>
|
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
#include <boost/filesystem/exception.hpp>
|
#include <boost/filesystem/exception.hpp>
|
||||||
|
|
||||||
#include "bittorrent.h"
|
|
||||||
#include "misc.h"
|
|
||||||
#include "downloadThread.h"
|
|
||||||
#include "deleteThread.h"
|
|
||||||
|
|
||||||
#define ETAS_MAX_VALUES 3
|
|
||||||
#define ETA_REFRESH_INTERVAL 10000
|
|
||||||
#define MAX_TRACKER_ERRORS 2
|
#define MAX_TRACKER_ERRORS 2
|
||||||
|
|
||||||
// Main constructor
|
// Main constructor
|
||||||
bittorrent::bittorrent() : timerScan(0), DHTEnabled(false){
|
bittorrent::bittorrent() : timerScan(0), DHTEnabled(false), preAllocateAll(false), addInPause(false), maxConnecsPerTorrent(500), maxUploadsPerTorrent(4), max_ratio(-1), UPnPEnabled(false), NATPMPEnabled(false), LSDEnabled(false), folderScanInterval(5) {
|
||||||
// To avoid some exceptions
|
// To avoid some exceptions
|
||||||
fs::path::default_name_check(fs::no_check);
|
fs::path::default_name_check(fs::no_check);
|
||||||
// Creating bittorrent session
|
// Creating bittorrent session
|
||||||
|
// Check if we should spoof azureus
|
||||||
|
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||||
|
if(settings.value(QString::fromUtf8("AzureusSpoof"), false).toBool()) {
|
||||||
|
s = new session(fingerprint("AZ", 3, 0, 5, 2));
|
||||||
|
} else {
|
||||||
s = new session(fingerprint("qB", VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, 0));
|
s = new session(fingerprint("qB", VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, 0));
|
||||||
|
}
|
||||||
// Set severity level of libtorrent session
|
// Set severity level of libtorrent session
|
||||||
s->set_severity_level(alert::info);
|
s->set_severity_level(alert::info);
|
||||||
// Enable LSD/UPnP/NAT-PMP
|
|
||||||
s->start_lsd();
|
|
||||||
s->start_natpmp();
|
|
||||||
s->start_upnp();
|
|
||||||
// Enabling metadata plugin
|
// Enabling metadata plugin
|
||||||
s->add_extension(&create_metadata_plugin);
|
s->add_extension(&create_metadata_plugin);
|
||||||
timerAlerts = new QTimer();
|
timerAlerts = new QTimer();
|
||||||
connect(timerAlerts, SIGNAL(timeout()), this, SLOT(readAlerts()));
|
connect(timerAlerts, SIGNAL(timeout()), this, SLOT(readAlerts()));
|
||||||
timerAlerts->start(3000);
|
timerAlerts->start(3000);
|
||||||
ETARefresher = new QTimer();
|
fastResumeSaver = new QTimer();
|
||||||
connect(ETARefresher, SIGNAL(timeout()), this, SLOT(updateETAs()));
|
connect(fastResumeSaver, SIGNAL(timeout()), this, SLOT(saveFastResumeAndRatioData()));
|
||||||
ETARefresher->start(ETA_REFRESH_INTERVAL);
|
fastResumeSaver->start(60000);
|
||||||
// To download from urls
|
// To download from urls
|
||||||
downloader = new downloadThread(this);
|
downloader = new downloadThread(this);
|
||||||
connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString)));
|
connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString)));
|
||||||
connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
|
connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
|
||||||
// File deleter (thread)
|
// File deleter (thread)
|
||||||
deleter = new deleteThread(this);
|
deleter = new deleteThread(this);
|
||||||
|
BigRatioTimer = 0;
|
||||||
|
filterParser = 0;
|
||||||
qDebug("* BTSession constructed");
|
qDebug("* BTSession constructed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main destructor
|
// Main destructor
|
||||||
bittorrent::~bittorrent() {
|
bittorrent::~bittorrent() {
|
||||||
|
qDebug("BTSession deletion");
|
||||||
|
// Set Session settings
|
||||||
|
session_settings ss;
|
||||||
|
ss.tracker_receive_timeout = 1;
|
||||||
|
ss.stop_tracker_timeout = 1;
|
||||||
|
ss.tracker_completion_timeout = 1;
|
||||||
|
ss.piece_timeout = 1;
|
||||||
|
ss.peer_timeout = 1;
|
||||||
|
ss.urlseed_timeout = 1;
|
||||||
|
s->set_settings(ss);
|
||||||
// Disable directory scanning
|
// Disable directory scanning
|
||||||
disableDirectoryScanning();
|
disableDirectoryScanning();
|
||||||
// Delete our objects
|
// Delete our objects
|
||||||
delete deleter;
|
delete deleter;
|
||||||
|
delete fastResumeSaver;
|
||||||
delete timerAlerts;
|
delete timerAlerts;
|
||||||
delete ETARefresher;
|
if(BigRatioTimer != 0)
|
||||||
|
delete BigRatioTimer;
|
||||||
|
if(filterParser != 0)
|
||||||
|
delete filterParser;
|
||||||
delete downloader;
|
delete downloader;
|
||||||
// Delete BT session
|
// Delete BT session
|
||||||
|
qDebug("Deleting session");
|
||||||
delete s;
|
delete s;
|
||||||
|
qDebug("Session deleted");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bittorrent::preAllocateAllFiles(bool b) {
|
||||||
|
bool change = (preAllocateAll != b);
|
||||||
|
if(change) {
|
||||||
|
qDebug("PreAllocateAll changed, reloading all torrents!");
|
||||||
|
preAllocateAll = b;
|
||||||
|
// Reload All unfinished torrents
|
||||||
|
QString hash;
|
||||||
|
foreach(hash, unfinishedTorrents) {
|
||||||
|
QTorrentHandle h = getTorrentHandle(hash);
|
||||||
|
if(!h.is_valid()) {
|
||||||
|
qDebug("/!\\ Error: Invalid handle");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
reloadTorrent(h, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bittorrent::deleteBigRatios() {
|
||||||
|
if(max_ratio == -1) return;
|
||||||
|
QString hash;
|
||||||
|
foreach(hash, finishedTorrents) {
|
||||||
|
QTorrentHandle h = getTorrentHandle(hash);
|
||||||
|
if(!h.is_valid()) {
|
||||||
|
qDebug("/!\\ Error: Invalid handle");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QString hash = h.hash();
|
||||||
|
if(getRealRatio(hash) > max_ratio) {
|
||||||
|
QString fileName = h.name();
|
||||||
|
deleteTorrent(hash);
|
||||||
|
emit torrent_ratio_deleted(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bittorrent::setDownloadLimit(QString hash, long val) {
|
void bittorrent::setDownloadLimit(QString hash, long val) {
|
||||||
@@ -104,36 +159,38 @@ void bittorrent::handleDownloadFailure(QString url, QString reason) {
|
|||||||
emit downloadFromUrlFailure(url, reason);
|
emit downloadFromUrlFailure(url, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bittorrent::updateETAs() {
|
void bittorrent::startTorrentsInPause(bool b) {
|
||||||
QString hash;
|
addInPause = b;
|
||||||
foreach(hash, unfinishedTorrents) {
|
|
||||||
QTorrentHandle h = getTorrentHandle(hash);
|
|
||||||
if(h.is_valid()) {
|
|
||||||
if(h.is_paused()) continue;
|
|
||||||
QString hash = h.hash();
|
|
||||||
QList<qlonglong> listEtas = ETAstats.value(hash, QList<qlonglong>());
|
|
||||||
if(listEtas.size() == ETAS_MAX_VALUES) {
|
|
||||||
listEtas.removeFirst();
|
|
||||||
}
|
|
||||||
if(h.download_payload_rate()) {
|
|
||||||
listEtas << (qlonglong)((h.actual_size()-h.total_done())/(double)h.download_payload_rate());
|
|
||||||
ETAstats[hash] = listEtas;
|
|
||||||
long moy = 0;
|
|
||||||
long val;
|
|
||||||
unsigned int nbETAs = listEtas.size();
|
|
||||||
Q_ASSERT(nbETAs);
|
|
||||||
foreach(val, listEtas) {
|
|
||||||
moy += (qlonglong)((double)val/(double)nbETAs);
|
|
||||||
Q_ASSERT(moy >= 0);
|
|
||||||
}
|
|
||||||
ETAs[hash] = moy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long bittorrent::getETA(QString hash) const{
|
// Calculate the ETA using GASA
|
||||||
return ETAs.value(hash, -1);
|
// GASA: global Average Speed Algorithm
|
||||||
|
qlonglong bittorrent::getETA(QString hash) const {
|
||||||
|
QTorrentHandle h = getTorrentHandle(hash);
|
||||||
|
if(!h.is_valid()) return -1;
|
||||||
|
switch(h.state()) {
|
||||||
|
case torrent_status::downloading:
|
||||||
|
case torrent_status::connecting_to_tracker: {
|
||||||
|
if(!TorrentsStartTime.contains(hash)) return -1;
|
||||||
|
int timeElapsed = TorrentsStartTime.value(hash).secsTo(QDateTime::currentDateTime());
|
||||||
|
double avg_speed;
|
||||||
|
if(timeElapsed) {
|
||||||
|
size_type data_origin = TorrentsStartData.value(hash, 0);
|
||||||
|
avg_speed = (double)(h.total_payload_download()-data_origin) / (double)timeElapsed;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(avg_speed) {
|
||||||
|
return (qlonglong) floor((double) (h.actual_size() - h.total_wanted_done()) / avg_speed);
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the torrent handle, given its hash
|
// Return the torrent handle, given its hash
|
||||||
@@ -154,6 +211,26 @@ bool bittorrent::isPaused(QString hash) const{
|
|||||||
return h.is_paused();
|
return h.is_paused();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int bittorrent::getFinishedPausedTorrentsNb() const {
|
||||||
|
unsigned int nbPaused = 0;
|
||||||
|
foreach(QString hash, finishedTorrents) {
|
||||||
|
if(isPaused(hash)) {
|
||||||
|
++nbPaused;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nbPaused;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int bittorrent::getUnfinishedPausedTorrentsNb() const {
|
||||||
|
unsigned int nbPaused = 0;
|
||||||
|
foreach(QString hash, unfinishedTorrents) {
|
||||||
|
if(isPaused(hash)) {
|
||||||
|
++nbPaused;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nbPaused;
|
||||||
|
}
|
||||||
|
|
||||||
// Delete a torrent from the session, given its hash
|
// Delete a torrent from the session, given its hash
|
||||||
// permanent = true means that the torrent will be removed from the hard-drive too
|
// permanent = true means that the torrent will be removed from the hard-drive too
|
||||||
void bittorrent::deleteTorrent(QString hash, bool permanent) {
|
void bittorrent::deleteTorrent(QString hash, bool permanent) {
|
||||||
@@ -165,9 +242,9 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
|
|||||||
}
|
}
|
||||||
QString savePath = h.save_path();
|
QString savePath = h.save_path();
|
||||||
QString fileName = h.name();
|
QString fileName = h.name();
|
||||||
QStringList files_path;
|
arborescence *files_arb = 0;
|
||||||
if(permanent){
|
if(permanent){
|
||||||
files_path = h.files_path();
|
files_arb = new arborescence(h.get_torrent_info());
|
||||||
}
|
}
|
||||||
// Remove it from session
|
// Remove it from session
|
||||||
s->remove_torrent(h.get_torrent_handle());
|
s->remove_torrent(h.get_torrent_handle());
|
||||||
@@ -180,9 +257,9 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
|
|||||||
foreach(file, files) {
|
foreach(file, files) {
|
||||||
torrentBackup.remove(file);
|
torrentBackup.remove(file);
|
||||||
}
|
}
|
||||||
// Remove it from ETAs hash tables
|
// Remove it from TorrentsStartTime hash table
|
||||||
ETAstats.remove(hash);
|
TorrentsStartTime.remove(hash);
|
||||||
ETAs.remove(hash);
|
TorrentsStartData.remove(hash);
|
||||||
// Remove tracker errors
|
// Remove tracker errors
|
||||||
trackersErrors.remove(hash);
|
trackersErrors.remove(hash);
|
||||||
// Remove it from ratio table
|
// Remove it from ratio table
|
||||||
@@ -198,12 +275,13 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
|
|||||||
std::cerr << "Error: Torrent " << hash.toStdString() << " is neither in finished or unfinished list\n";
|
std::cerr << "Error: Torrent " << hash.toStdString() << " is neither in finished or unfinished list\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(permanent) {
|
if(permanent && files_arb != 0) {
|
||||||
// Remove from Hard drive
|
// Remove from Hard drive
|
||||||
qDebug("Removing this on hard drive: %s", qPrintable(savePath+QDir::separator()+fileName));
|
qDebug("Removing this on hard drive: %s", qPrintable(savePath+QDir::separator()+fileName));
|
||||||
// Deleting in a thread to avoid GUI freeze
|
// Deleting in a thread to avoid GUI freeze
|
||||||
deleter->deleteTorrent(savePath, files_path);
|
deleter->deleteTorrent(savePath, files_arb);
|
||||||
}
|
}
|
||||||
|
emit deletedTorrent(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a list of hashes for the finished torrents
|
// Return a list of hashes for the finished torrents
|
||||||
@@ -230,6 +308,9 @@ void bittorrent::setUnfinishedTorrent(QString hash) {
|
|||||||
}
|
}
|
||||||
if(!unfinishedTorrents.contains(hash)) {
|
if(!unfinishedTorrents.contains(hash)) {
|
||||||
unfinishedTorrents << hash;
|
unfinishedTorrents << hash;
|
||||||
|
QTorrentHandle h = getTorrentHandle(hash);
|
||||||
|
TorrentsStartData[hash] = h.total_payload_download();
|
||||||
|
TorrentsStartTime[hash] = QDateTime::currentDateTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,9 +328,11 @@ void bittorrent::setFinishedTorrent(QString hash) {
|
|||||||
if(index != -1) {
|
if(index != -1) {
|
||||||
unfinishedTorrents.removeAt(index);
|
unfinishedTorrents.removeAt(index);
|
||||||
}
|
}
|
||||||
// Remove it from ETAs hash tables
|
// Remove it from TorrentsStartTime hash table
|
||||||
ETAstats.remove(hash);
|
TorrentsStartTime.remove(hash);
|
||||||
ETAs.remove(hash);
|
TorrentsStartData.remove(hash);
|
||||||
|
// Save fast resume data
|
||||||
|
saveFastResumeAndRatioData(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause a running torrent
|
// Pause a running torrent
|
||||||
@@ -259,7 +342,10 @@ bool bittorrent::pauseTorrent(QString hash) {
|
|||||||
if(h.is_valid() && !h.is_paused()) {
|
if(h.is_valid() && !h.is_paused()) {
|
||||||
h.pause();
|
h.pause();
|
||||||
change = true;
|
change = true;
|
||||||
|
// Save fast resume data
|
||||||
|
saveFastResumeAndRatioData(hash);
|
||||||
qDebug("Torrent paused successfully");
|
qDebug("Torrent paused successfully");
|
||||||
|
emit pausedTorrent(hash);
|
||||||
}else{
|
}else{
|
||||||
if(!h.is_valid()) {
|
if(!h.is_valid()) {
|
||||||
qDebug("Could not pause torrent %s, reason: invalid", hash.toUtf8().data());
|
qDebug("Could not pause torrent %s, reason: invalid", hash.toUtf8().data());
|
||||||
@@ -273,9 +359,9 @@ bool bittorrent::pauseTorrent(QString hash) {
|
|||||||
paused_file.open(QIODevice::WriteOnly | QIODevice::Text);
|
paused_file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||||
paused_file.close();
|
paused_file.close();
|
||||||
}
|
}
|
||||||
// Remove it from ETAs hash tables
|
// Remove it from TorrentsStartTime hash table
|
||||||
ETAstats.remove(hash);
|
TorrentsStartTime.remove(hash);
|
||||||
ETAs.remove(hash);
|
TorrentsStartData.remove(hash);
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,8 +370,12 @@ bool bittorrent::resumeTorrent(QString hash) {
|
|||||||
bool success = false;
|
bool success = false;
|
||||||
QTorrentHandle h = getTorrentHandle(hash);
|
QTorrentHandle h = getTorrentHandle(hash);
|
||||||
if(h.is_valid() && h.is_paused()) {
|
if(h.is_valid() && h.is_paused()) {
|
||||||
|
// Save Addition DateTime
|
||||||
|
TorrentsStartData[hash] = h.total_payload_download();
|
||||||
|
TorrentsStartTime[hash] = QDateTime::currentDateTime();
|
||||||
h.resume();
|
h.resume();
|
||||||
success = true;
|
success = true;
|
||||||
|
emit resumedTorrent(hash);
|
||||||
}
|
}
|
||||||
// Delete .paused file
|
// Delete .paused file
|
||||||
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused"))
|
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused"))
|
||||||
@@ -298,6 +388,18 @@ bool bittorrent::resumeTorrent(QString hash) {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bittorrent::pauseAllTorrents() {
|
||||||
|
QStringList list = getUnfinishedTorrents() + getFinishedTorrents();
|
||||||
|
foreach(QString hash, list)
|
||||||
|
pauseTorrent(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bittorrent::resumeAllTorrents() {
|
||||||
|
QStringList list = getUnfinishedTorrents() + getFinishedTorrents();
|
||||||
|
foreach(QString hash, list)
|
||||||
|
resumeTorrent(hash);
|
||||||
|
}
|
||||||
|
|
||||||
void bittorrent::loadWebSeeds(QString hash) {
|
void bittorrent::loadWebSeeds(QString hash) {
|
||||||
QFile urlseeds_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".urlseeds");
|
QFile urlseeds_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".urlseeds");
|
||||||
if(!urlseeds_file.open(QIODevice::ReadOnly | QIODevice::Text)) return;
|
if(!urlseeds_file.open(QIODevice::ReadOnly | QIODevice::Text)) return;
|
||||||
@@ -330,7 +432,7 @@ void bittorrent::loadWebSeeds(QString hash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a torrent to the bittorrent session
|
// Add a torrent to the bittorrent session
|
||||||
void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url, bool resumed) {
|
||||||
QTorrentHandle h;
|
QTorrentHandle h;
|
||||||
entry resume_data;
|
entry resume_data;
|
||||||
bool fastResume=false;
|
bool fastResume=false;
|
||||||
@@ -346,11 +448,11 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Processing torrents
|
// Processing torrents
|
||||||
file = path.trimmed().replace("file://", "");
|
file = path.trimmed().replace("file://", "", Qt::CaseInsensitive);
|
||||||
if(file.isEmpty()) {
|
if(file.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Q_ASSERT(!file.startsWith("http://") && !file.startsWith("https://") && !file.startsWith("ftp://"));
|
Q_ASSERT(!file.startsWith("http://", Qt::CaseInsensitive) && !file.startsWith("https://", Qt::CaseInsensitive) && !file.startsWith("ftp://", Qt::CaseInsensitive));
|
||||||
qDebug("Adding %s to download list", file.toUtf8().data());
|
qDebug("Adding %s to download list", file.toUtf8().data());
|
||||||
std::ifstream in(file.toUtf8().data(), std::ios_base::binary);
|
std::ifstream in(file.toUtf8().data(), std::ios_base::binary);
|
||||||
in.unsetf(std::ios_base::skipws);
|
in.unsetf(std::ios_base::skipws);
|
||||||
@@ -358,27 +460,18 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||||||
// Decode torrent file
|
// Decode torrent file
|
||||||
entry e = bdecode(std::istream_iterator<char>(in), std::istream_iterator<char>());
|
entry e = bdecode(std::istream_iterator<char>(in), std::istream_iterator<char>());
|
||||||
// Getting torrent file informations
|
// Getting torrent file informations
|
||||||
torrent_info t(e);
|
boost::intrusive_ptr<torrent_info> t(new torrent_info(e));
|
||||||
qDebug(" -> Hash: %s", misc::toString(t.info_hash()).c_str());
|
qDebug(" -> Hash: %s", misc::toString(t->info_hash()).c_str());
|
||||||
qDebug(" -> Name: %s", t.name().c_str());
|
qDebug(" -> Name: %s", t->name().c_str());
|
||||||
QString hash = misc::toQString(t.info_hash());
|
QString hash = misc::toQString(t->info_hash());
|
||||||
if(file.startsWith(torrentBackup.path())) {
|
if(file.startsWith(torrentBackup.path())) {
|
||||||
QFileInfo fi(file);
|
QFileInfo fi(file);
|
||||||
QString old_hash = fi.baseName();
|
QString old_hash = fi.baseName();
|
||||||
if(old_hash != hash){
|
if(old_hash != hash){
|
||||||
qDebug("* ERROR: Strange, hash changed from %s to %s", old_hash.toUtf8().data(), hash.toUtf8().data());
|
qDebug("* ERROR: Strange, hash changed from %s to %s", old_hash.toUtf8().data(), hash.toUtf8().data());
|
||||||
// QStringList filters;
|
|
||||||
// filters << old_hash+".*";
|
|
||||||
// QStringList files = torrentBackup.entryList(filters, QDir::Files, QDir::Unsorted);
|
|
||||||
// QString my_f;
|
|
||||||
// foreach(my_f, files) {
|
|
||||||
// qDebug("* deleting %s", my_f.toUtf8().data());
|
|
||||||
// torrentBackup.remove(my_f);
|
|
||||||
// }
|
|
||||||
// return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(s->find_torrent(t.info_hash()).is_valid()) {
|
if(s->find_torrent(t->info_hash()).is_valid()) {
|
||||||
qDebug("/!\\ Torrent is already in download list");
|
qDebug("/!\\ Torrent is already in download list");
|
||||||
// Update info Bar
|
// Update info Bar
|
||||||
if(!fromScanDir) {
|
if(!fromScanDir) {
|
||||||
@@ -409,12 +502,12 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||||||
}
|
}
|
||||||
QString savePath = getSavePath(hash);
|
QString savePath = getSavePath(hash);
|
||||||
// Adding files to bittorrent session
|
// Adding files to bittorrent session
|
||||||
if(has_filtered_files(hash)) {
|
if(preAllocateAll) {
|
||||||
h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, false, true);
|
h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, storage_mode_allocate, true);
|
||||||
qDebug(" -> Full allocation mode");
|
qDebug(" -> Full allocation mode");
|
||||||
}else{
|
}else{
|
||||||
h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, true, true);
|
h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, storage_mode_sparse, true);
|
||||||
qDebug(" -> Compact allocation mode");
|
qDebug(" -> Sparse allocation mode");
|
||||||
}
|
}
|
||||||
if(!h.is_valid()) {
|
if(!h.is_valid()) {
|
||||||
// No need to keep on, it failed.
|
// No need to keep on, it failed.
|
||||||
@@ -423,9 +516,10 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||||||
if(!from_url.isNull()) QFile::remove(file);
|
if(!from_url.isNull()) QFile::remove(file);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Is this really useful and appropriate ?
|
// Connections limit per torrent
|
||||||
//h.set_max_connections(60);
|
h.set_max_connections(maxConnecsPerTorrent);
|
||||||
h.set_max_uploads(-1);
|
// Uploads limit per torrent
|
||||||
|
h.set_max_uploads(maxUploadsPerTorrent);
|
||||||
// Load filtered files
|
// Load filtered files
|
||||||
loadFilesPriorities(h);
|
loadFilesPriorities(h);
|
||||||
// Load custom url seeds
|
// Load custom url seeds
|
||||||
@@ -450,13 +544,13 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||||||
QFile::copy(file, newFile);
|
QFile::copy(file, newFile);
|
||||||
}
|
}
|
||||||
// Pause torrent if it was paused last time
|
// Pause torrent if it was paused last time
|
||||||
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) {
|
if((!resumed && addInPause) || QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) {
|
||||||
torrentsToPauseAfterChecking << hash;
|
torrentsToPauseAfterChecking << hash;
|
||||||
qDebug("Adding a torrent to the torrentsToPauseAfterChecking list");
|
qDebug("Adding a torrent to the torrentsToPauseAfterChecking list");
|
||||||
}
|
}
|
||||||
// Incremental download
|
// Incremental download
|
||||||
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".incremental")) {
|
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".incremental")) {
|
||||||
qDebug("Incremental download enabled for %s", t.name().c_str());
|
qDebug("Incremental download enabled for %s", t->name().c_str());
|
||||||
h.set_sequenced_download_threshold(1);
|
h.set_sequenced_download_threshold(1);
|
||||||
}
|
}
|
||||||
// Start torrent because it was added in paused state
|
// Start torrent because it was added in paused state
|
||||||
@@ -483,6 +577,7 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||||||
// Display warning to tell user we can't decode the torrent file
|
// Display warning to tell user we can't decode the torrent file
|
||||||
if(!from_url.isNull()) {
|
if(!from_url.isNull()) {
|
||||||
emit invalidTorrent(from_url);
|
emit invalidTorrent(from_url);
|
||||||
|
QFile::remove(file);
|
||||||
}else{
|
}else{
|
||||||
emit invalidTorrent(file);
|
emit invalidTorrent(file);
|
||||||
}
|
}
|
||||||
@@ -497,6 +592,24 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
|
|||||||
// Display warning to tell user we can't decode the torrent file
|
// Display warning to tell user we can't decode the torrent file
|
||||||
if(!from_url.isNull()) {
|
if(!from_url.isNull()) {
|
||||||
emit invalidTorrent(from_url);
|
emit invalidTorrent(from_url);
|
||||||
|
qDebug("File path is: %s", file.toUtf8().data());
|
||||||
|
QFile::remove(file);
|
||||||
|
}else{
|
||||||
|
emit invalidTorrent(file);
|
||||||
|
}
|
||||||
|
if(fromScanDir) {
|
||||||
|
// Remove .corrupt file in case it already exists
|
||||||
|
QFile::remove(file+".corrupt");
|
||||||
|
//Rename file extension so that it won't display error message more than once
|
||||||
|
QFile::rename(file,file+".corrupt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception& e) {
|
||||||
|
std::cerr << "Could not decode file, reason: " << e.what() << '\n';
|
||||||
|
// Display warning to tell user we can't decode the torrent file
|
||||||
|
if(!from_url.isNull()) {
|
||||||
|
emit invalidTorrent(from_url);
|
||||||
|
QFile::remove(file);
|
||||||
}else{
|
}else{
|
||||||
emit invalidTorrent(file);
|
emit invalidTorrent(file);
|
||||||
}
|
}
|
||||||
@@ -538,13 +651,92 @@ void bittorrent::setMaxConnections(int maxConnec) {
|
|||||||
s->set_max_connections(maxConnec);
|
s->set_max_connections(maxConnec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bittorrent::setMaxConnectionsPerTorrent(int max) {
|
||||||
|
maxConnecsPerTorrent = max;
|
||||||
|
// Apply this to all session torrents
|
||||||
|
std::vector<torrent_handle> handles = s->get_torrents();
|
||||||
|
unsigned int nbHandles = handles.size();
|
||||||
|
for(unsigned int i=0; i<nbHandles; ++i) {
|
||||||
|
QTorrentHandle h = handles[i];
|
||||||
|
if(!h.is_valid()) {
|
||||||
|
qDebug("/!\\ Error: Invalid handle");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
h.set_max_connections(max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bittorrent::setMaxUploadsPerTorrent(int max) {
|
||||||
|
maxUploadsPerTorrent = max;
|
||||||
|
// Apply this to all session torrents
|
||||||
|
std::vector<torrent_handle> handles = s->get_torrents();
|
||||||
|
unsigned int nbHandles = handles.size();
|
||||||
|
for(unsigned int i=0; i<nbHandles; ++i) {
|
||||||
|
QTorrentHandle h = handles[i];
|
||||||
|
if(!h.is_valid()) {
|
||||||
|
qDebug("/!\\ Error: Invalid handle");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
h.set_max_uploads(max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return DHT state
|
// Return DHT state
|
||||||
bool bittorrent::isDHTEnabled() const{
|
bool bittorrent::isDHTEnabled() const{
|
||||||
return DHTEnabled;
|
return DHTEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bittorrent::enableUPnP(bool b) {
|
||||||
|
if(b) {
|
||||||
|
if(!UPnPEnabled) {
|
||||||
|
qDebug("Enabling UPnP");
|
||||||
|
s->start_upnp();
|
||||||
|
UPnPEnabled = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(UPnPEnabled) {
|
||||||
|
qDebug("Disabling UPnP");
|
||||||
|
s->stop_upnp();
|
||||||
|
UPnPEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bittorrent::enableNATPMP(bool b) {
|
||||||
|
if(b) {
|
||||||
|
if(!NATPMPEnabled) {
|
||||||
|
qDebug("Enabling NAT-PMP");
|
||||||
|
s->start_natpmp();
|
||||||
|
NATPMPEnabled = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(NATPMPEnabled) {
|
||||||
|
qDebug("Disabling NAT-PMP");
|
||||||
|
s->stop_natpmp();
|
||||||
|
NATPMPEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bittorrent::enableLSD(bool b) {
|
||||||
|
if(b) {
|
||||||
|
if(!LSDEnabled) {
|
||||||
|
qDebug("Enabling LSD");
|
||||||
|
s->start_lsd();
|
||||||
|
LSDEnabled = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(LSDEnabled) {
|
||||||
|
qDebug("Disabling LSD");
|
||||||
|
s->stop_lsd();
|
||||||
|
LSDEnabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Enable DHT
|
// Enable DHT
|
||||||
void bittorrent::enableDHT() {
|
bool bittorrent::enableDHT(bool b) {
|
||||||
|
if(b) {
|
||||||
if(!DHTEnabled) {
|
if(!DHTEnabled) {
|
||||||
boost::filesystem::ifstream dht_state_file((misc::qBittorrentPath()+QString::fromUtf8("dht_state")).toUtf8().data(), std::ios_base::binary);
|
boost::filesystem::ifstream dht_state_file((misc::qBittorrentPath()+QString::fromUtf8("dht_state")).toUtf8().data(), std::ios_base::binary);
|
||||||
dht_state_file.unsetf(std::ios_base::skipws);
|
dht_state_file.unsetf(std::ios_base::skipws);
|
||||||
@@ -552,22 +744,26 @@ void bittorrent::enableDHT() {
|
|||||||
try{
|
try{
|
||||||
dht_state = bdecode(std::istream_iterator<char>(dht_state_file), std::istream_iterator<char>());
|
dht_state = bdecode(std::istream_iterator<char>(dht_state_file), std::istream_iterator<char>());
|
||||||
}catch (std::exception&) {}
|
}catch (std::exception&) {}
|
||||||
|
try {
|
||||||
s->start_dht(dht_state);
|
s->start_dht(dht_state);
|
||||||
s->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881));
|
s->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881));
|
||||||
s->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881));
|
s->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881));
|
||||||
s->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), 6881));
|
s->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), 6881));
|
||||||
DHTEnabled = true;
|
DHTEnabled = true;
|
||||||
qDebug("DHT enabled");
|
qDebug("DHT enabled");
|
||||||
|
}catch(std::exception e) {
|
||||||
|
qDebug("Could not enable DHT, reason: %s", e.what());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
// Disable DHT
|
|
||||||
void bittorrent::disableDHT() {
|
|
||||||
if(DHTEnabled) {
|
if(DHTEnabled) {
|
||||||
DHTEnabled = false;
|
DHTEnabled = false;
|
||||||
s->stop_dht();
|
s->stop_dht();
|
||||||
qDebug("DHT disabled");
|
qDebug("DHT disabled");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bittorrent::saveTorrentSpeedLimits(QString hash) {
|
void bittorrent::saveTorrentSpeedLimits(QString hash) {
|
||||||
@@ -605,6 +801,7 @@ void bittorrent::loadTorrentSpeedLimits(QString hash) {
|
|||||||
// Read pieces priorities from .priorities file
|
// Read pieces priorities from .priorities file
|
||||||
// and ask QTorrentHandle to consider them
|
// and ask QTorrentHandle to consider them
|
||||||
void bittorrent::loadFilesPriorities(QTorrentHandle &h) {
|
void bittorrent::loadFilesPriorities(QTorrentHandle &h) {
|
||||||
|
qDebug("Applying pieces priorities");
|
||||||
if(!h.is_valid()) {
|
if(!h.is_valid()) {
|
||||||
qDebug("/!\\ Error: Invalid handle");
|
qDebug("/!\\ Error: Invalid handle");
|
||||||
return;
|
return;
|
||||||
@@ -633,7 +830,7 @@ void bittorrent::loadFilesPriorities(QTorrentHandle &h) {
|
|||||||
if( priority < 0 || priority > 7) {
|
if( priority < 0 || priority > 7) {
|
||||||
priority = 1;
|
priority = 1;
|
||||||
}
|
}
|
||||||
//qDebug("Setting piece piority to %d", priority);
|
qDebug("Setting piece piority to %d", priority);
|
||||||
v.push_back(priority);
|
v.push_back(priority);
|
||||||
}
|
}
|
||||||
h.prioritize_files(v);
|
h.prioritize_files(v);
|
||||||
@@ -660,7 +857,11 @@ void bittorrent::loadDownloadUploadForTorrent(QString hash) {
|
|||||||
QPair<size_type,size_type> downUp;
|
QPair<size_type,size_type> downUp;
|
||||||
downUp.first = (size_type)data_list.at(0).toLongLong();
|
downUp.first = (size_type)data_list.at(0).toLongLong();
|
||||||
downUp.second = (size_type)data_list.at(1).toLongLong();
|
downUp.second = (size_type)data_list.at(1).toLongLong();
|
||||||
Q_ASSERT(downUp.first >= 0 && downUp.second >= 0);
|
if(downUp.first < 0 || downUp.second < 0) {
|
||||||
|
qDebug("** Overflow in ratio!!! fixing...");
|
||||||
|
downUp.first = 0;
|
||||||
|
downUp.second = 0;
|
||||||
|
}
|
||||||
ratioData[hash] = downUp;
|
ratioData[hash] = downUp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,10 +916,26 @@ void bittorrent::saveDownloadUploadForTorrent(QString hash) {
|
|||||||
ratio_file.close();
|
ratio_file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save fastresume data for all torrents
|
// Only save fast resume data for unfinished and unpaused torrents (Optimization)
|
||||||
// and remove them from the session
|
// Called periodically and on exit
|
||||||
void bittorrent::saveFastResumeAndRatioData() {
|
void bittorrent::saveFastResumeAndRatioData() {
|
||||||
qDebug("Saving fast resume and ratio data");
|
QString hash;
|
||||||
|
QStringList hashes = getUnfinishedTorrents();
|
||||||
|
foreach(hash, hashes) {
|
||||||
|
QTorrentHandle h = getTorrentHandle(hash);
|
||||||
|
if(!h.is_valid()) {
|
||||||
|
qDebug("/!\\ Error: Invalid handle");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(h.is_paused()) {
|
||||||
|
// Do not need to save fast resume data for paused torrents
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
saveFastResumeAndRatioData(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bittorrent::saveFastResumeAndRatioData(QString hash) {
|
||||||
QString file;
|
QString file;
|
||||||
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
|
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
|
||||||
// Checking if torrentBackup Dir exists
|
// Checking if torrentBackup Dir exists
|
||||||
@@ -726,35 +943,13 @@ void bittorrent::saveFastResumeAndRatioData() {
|
|||||||
if(! torrentBackup.exists()) {
|
if(! torrentBackup.exists()) {
|
||||||
torrentBackup.mkpath(torrentBackup.path());
|
torrentBackup.mkpath(torrentBackup.path());
|
||||||
}
|
}
|
||||||
// Pause torrents
|
|
||||||
std::vector<torrent_handle> handles = s->get_torrents();
|
|
||||||
for(unsigned int i=0; i<handles.size(); ++i) {
|
|
||||||
QTorrentHandle h = handles[i];
|
|
||||||
if(!h.is_valid()) {
|
|
||||||
qDebug("/!\\ Error: Invalid handle");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Pause download (needed before fast resume writing)
|
|
||||||
if(!h.is_paused()){
|
|
||||||
waitingForPause << h.hash();
|
|
||||||
h.pause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Write fast resume data
|
|
||||||
for(unsigned int i=0; i<handles.size(); ++i) {
|
|
||||||
QTorrentHandle h = handles[i];
|
|
||||||
if(!h.is_valid()) {
|
|
||||||
qDebug("/!\\ Error: Invalid handle");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
QString hash = h.hash();
|
|
||||||
while(waitingForPause.contains(hash)) {
|
|
||||||
//qDebug("Sleeping while waiting that %s is paused", misc::toString(h.info_hash()).c_str());
|
|
||||||
SleeperThread::msleep(300);
|
|
||||||
readAlerts();
|
|
||||||
}
|
|
||||||
// Extracting resume data
|
// Extracting resume data
|
||||||
if (h.has_metadata()) {
|
QTorrentHandle h = getTorrentHandle(hash);
|
||||||
|
if(!h.is_valid()) {
|
||||||
|
qDebug("/!\\ Error: Invalid handle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (h.has_metadata() && h.state() != torrent_status::checking_files && h.state() != torrent_status::queued_for_checking) {
|
||||||
if(QFile::exists(torrentBackup.path()+QDir::separator()+hash+".torrent")) {
|
if(QFile::exists(torrentBackup.path()+QDir::separator()+hash+".torrent")) {
|
||||||
// Remove old .fastresume data in case it exists
|
// Remove old .fastresume data in case it exists
|
||||||
QFile::remove(torrentBackup.path()+QDir::separator()+hash + ".fastresume");
|
QFile::remove(torrentBackup.path()+QDir::separator()+hash + ".fastresume");
|
||||||
@@ -767,13 +962,7 @@ void bittorrent::saveFastResumeAndRatioData() {
|
|||||||
}
|
}
|
||||||
// Save ratio data
|
// Save ratio data
|
||||||
saveDownloadUploadForTorrent(hash);
|
saveDownloadUploadForTorrent(hash);
|
||||||
// Save trackers
|
|
||||||
saveTrackerFile(hash);
|
|
||||||
}
|
}
|
||||||
// Remove torrent
|
|
||||||
s->remove_torrent(h.get_torrent_handle());
|
|
||||||
}
|
|
||||||
qDebug("Fast resume and ratio data saved");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bittorrent::isFilePreviewPossible(QString hash) const{
|
bool bittorrent::isFilePreviewPossible(QString hash) const{
|
||||||
@@ -816,13 +1005,22 @@ void bittorrent::setDefaultSavePath(QString savepath) {
|
|||||||
defaultSavePath = savepath;
|
defaultSavePath = savepath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bittorrent::setTimerScanInterval(int secs) {
|
||||||
|
if(folderScanInterval != secs) {
|
||||||
|
folderScanInterval = secs;
|
||||||
|
if(!scan_dir.isNull()) {
|
||||||
|
timerScan->start(folderScanInterval*1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Enable directory scanning
|
// Enable directory scanning
|
||||||
void bittorrent::enableDirectoryScanning(QString _scan_dir) {
|
void bittorrent::enableDirectoryScanning(QString _scan_dir) {
|
||||||
if(!_scan_dir.isEmpty()) {
|
if(!_scan_dir.isEmpty()) {
|
||||||
scan_dir = _scan_dir;
|
scan_dir = _scan_dir;
|
||||||
timerScan = new QTimer(this);
|
timerScan = new QTimer(this);
|
||||||
connect(timerScan, SIGNAL(timeout()), this, SLOT(scanDirectory()));
|
connect(timerScan, SIGNAL(timeout()), this, SLOT(scanDirectory()));
|
||||||
timerScan->start(5000);
|
timerScan->start(folderScanInterval*1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -865,6 +1063,11 @@ void bittorrent::setUploadRateLimit(long rate) {
|
|||||||
// libtorrent allow to adjust ratio for each torrent
|
// libtorrent allow to adjust ratio for each torrent
|
||||||
// This function will apply to same ratio to all torrents
|
// This function will apply to same ratio to all torrents
|
||||||
void bittorrent::setGlobalRatio(float ratio) {
|
void bittorrent::setGlobalRatio(float ratio) {
|
||||||
|
if(ratio != -1 && ratio < 1.) ratio = 1.;
|
||||||
|
if(ratio == -1) {
|
||||||
|
// 0 means unlimited for libtorrent
|
||||||
|
ratio = 0;
|
||||||
|
}
|
||||||
std::vector<torrent_handle> handles = s->get_torrents();
|
std::vector<torrent_handle> handles = s->get_torrents();
|
||||||
unsigned int nbHandles = handles.size();
|
unsigned int nbHandles = handles.size();
|
||||||
for(unsigned int i=0; i<nbHandles; ++i) {
|
for(unsigned int i=0; i<nbHandles; ++i) {
|
||||||
@@ -877,6 +1080,28 @@ void bittorrent::setGlobalRatio(float ratio) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Torrents will a ratio superior to the given value will
|
||||||
|
// be automatically deleted
|
||||||
|
void bittorrent::setDeleteRatio(float ratio) {
|
||||||
|
if(ratio != -1 && ratio < 1.) ratio = 1.;
|
||||||
|
if(max_ratio == -1 && ratio != -1) {
|
||||||
|
Q_ASSERT(!BigRatioTimer);
|
||||||
|
BigRatioTimer = new QTimer(this);
|
||||||
|
connect(BigRatioTimer, SIGNAL(timeout()), this, SLOT(deleteBigRatios()));
|
||||||
|
BigRatioTimer->start(5000);
|
||||||
|
} else {
|
||||||
|
if(max_ratio != -1 && ratio == -1) {
|
||||||
|
delete BigRatioTimer;
|
||||||
|
BigRatioTimer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(max_ratio != ratio) {
|
||||||
|
max_ratio = ratio;
|
||||||
|
qDebug("* Set deleteRatio to %.1f", max_ratio);
|
||||||
|
deleteBigRatios();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool bittorrent::loadTrackerFile(QString hash) {
|
bool bittorrent::loadTrackerFile(QString hash) {
|
||||||
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
|
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
|
||||||
QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers");
|
QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers");
|
||||||
@@ -895,6 +1120,7 @@ bool bittorrent::loadTrackerFile(QString hash) {
|
|||||||
if(trackers.size() != 0) {
|
if(trackers.size() != 0) {
|
||||||
QTorrentHandle h = getTorrentHandle(hash);
|
QTorrentHandle h = getTorrentHandle(hash);
|
||||||
h.replace_trackers(trackers);
|
h.replace_trackers(trackers);
|
||||||
|
h.force_reannounce();
|
||||||
return true;
|
return true;
|
||||||
}else{
|
}else{
|
||||||
return false;
|
return false;
|
||||||
@@ -902,6 +1128,7 @@ bool bittorrent::loadTrackerFile(QString hash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bittorrent::saveTrackerFile(QString hash) {
|
void bittorrent::saveTrackerFile(QString hash) {
|
||||||
|
qDebug("Saving tracker file for %s", hash.toUtf8().data());
|
||||||
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
|
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
|
||||||
QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers");
|
QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers");
|
||||||
if(tracker_file.exists()) {
|
if(tracker_file.exists()) {
|
||||||
@@ -933,15 +1160,25 @@ void bittorrent::setDHTPort(int dht_port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enable IP Filtering
|
// Enable IP Filtering
|
||||||
void bittorrent::enableIPFilter(ip_filter filter) {
|
void bittorrent::enableIPFilter(QString filter) {
|
||||||
qDebug("Enabling IPFiler");
|
qDebug("Enabling IPFiler");
|
||||||
s->set_ip_filter(filter);
|
if(!filterParser) {
|
||||||
|
filterParser = new FilterParserThread(this, s);
|
||||||
|
}
|
||||||
|
if(filterPath.isEmpty() || filterPath != filter) {
|
||||||
|
filterPath = filter;
|
||||||
|
filterParser->processFilterFile(filter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable IP Filtering
|
// Disable IP Filtering
|
||||||
void bittorrent::disableIPFilter() {
|
void bittorrent::disableIPFilter() {
|
||||||
qDebug("Disabling IPFilter");
|
qDebug("Disabling IPFilter");
|
||||||
s->set_ip_filter(ip_filter());
|
s->set_ip_filter(ip_filter());
|
||||||
|
if(filterParser) {
|
||||||
|
delete filterParser;
|
||||||
|
}
|
||||||
|
filterPath = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set BT session settings (user_agent)
|
// Set BT session settings (user_agent)
|
||||||
@@ -980,6 +1217,7 @@ void bittorrent::readAlerts() {
|
|||||||
}
|
}
|
||||||
else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a.get())) {
|
else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a.get())) {
|
||||||
QTorrentHandle h(p->handle);
|
QTorrentHandle h(p->handle);
|
||||||
|
qDebug("File Error: %s", p->msg().c_str());
|
||||||
if(h.is_valid())
|
if(h.is_valid())
|
||||||
emit fullDiskError(h);
|
emit fullDiskError(h);
|
||||||
}
|
}
|
||||||
@@ -991,33 +1229,36 @@ void bittorrent::readAlerts() {
|
|||||||
// Level: fatal
|
// Level: fatal
|
||||||
QTorrentHandle h(p->handle);
|
QTorrentHandle h(p->handle);
|
||||||
if(h.is_valid()){
|
if(h.is_valid()){
|
||||||
QString hash = h.hash();
|
|
||||||
QList<QPair<QString, QString> > errors = trackersErrors.value(hash, QList<QPair<QString, QString> >());
|
|
||||||
if(errors.size() > MAX_TRACKER_ERRORS)
|
|
||||||
errors.removeAt(0);
|
|
||||||
errors << QPair<QString,QString>(QTime::currentTime().toString("hh:mm:ss"), QString::fromUtf8(a->msg().c_str()));
|
|
||||||
trackersErrors[hash] = errors;
|
|
||||||
// Authentication
|
// Authentication
|
||||||
if(p->status_code == 401) {
|
if(p->status_code != 401) {
|
||||||
|
QString hash = h.hash();
|
||||||
|
qDebug("Received a tracker error for %s", p->url.c_str());
|
||||||
|
QHash<QString, QString> errors = trackersErrors.value(hash, QHash<QString, QString>());
|
||||||
|
// p->url requires at least libtorrent v0.13.1
|
||||||
|
errors[misc::toQString(p->url)] = QString::fromUtf8(a->msg().c_str());
|
||||||
|
trackersErrors[hash] = errors;
|
||||||
|
} else {
|
||||||
emit trackerAuthenticationRequired(h);
|
emit trackerAuthenticationRequired(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (torrent_paused_alert* p = dynamic_cast<torrent_paused_alert*>(a.get())) {
|
else if (tracker_reply_alert* p = dynamic_cast<tracker_reply_alert*>(a.get())) {
|
||||||
QTorrentHandle h(p->handle);
|
QTorrentHandle h(p->handle);
|
||||||
if(h.is_valid()){
|
if(h.is_valid()){
|
||||||
|
qDebug("Received a tracker reply from %s", (const char*)h.current_tracker().toUtf8());
|
||||||
QString hash = h.hash();
|
QString hash = h.hash();
|
||||||
qDebug("Received torrent_paused_alert for %s", hash.toUtf8().data());
|
QHash<QString, QString> errors = trackersErrors.value(hash, QHash<QString, QString>());
|
||||||
int index = waitingForPause.indexOf(hash);
|
// p->url requires at least libtorrent v0.13.1
|
||||||
if(index != -1){
|
errors.remove(h.current_tracker());
|
||||||
waitingForPause.removeAt(index);
|
trackersErrors[hash] = errors;
|
||||||
}
|
|
||||||
index = reloadingTorrents.indexOf(hash);
|
|
||||||
if(index != -1) {
|
|
||||||
reloadingTorrents.removeAt(index);
|
|
||||||
reloadTorrent(h);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (portmap_error_alert* p = dynamic_cast<portmap_error_alert*>(a.get())) {
|
||||||
|
emit UPnPError(QString(p->msg().c_str()));
|
||||||
|
}
|
||||||
|
else if (portmap_alert* p = dynamic_cast<portmap_alert*>(a.get())) {
|
||||||
|
qDebug("UPnP Success, msg: %s", p->msg().c_str());
|
||||||
|
emit UPnPSuccess(QString(p->msg().c_str()));
|
||||||
}
|
}
|
||||||
else if (peer_blocked_alert* p = dynamic_cast<peer_blocked_alert*>(a.get())) {
|
else if (peer_blocked_alert* p = dynamic_cast<peer_blocked_alert*>(a.get())) {
|
||||||
emit peerBlocked(QString::fromUtf8(p->ip.to_string().c_str()));
|
emit peerBlocked(QString::fromUtf8(p->ip.to_string().c_str()));
|
||||||
@@ -1043,6 +1284,10 @@ void bittorrent::readAlerts() {
|
|||||||
// Pause torrent
|
// Pause torrent
|
||||||
pauseTorrent(hash);
|
pauseTorrent(hash);
|
||||||
qDebug("%s was paused after checking", hash.toUtf8().data());
|
qDebug("%s was paused after checking", hash.toUtf8().data());
|
||||||
|
} else {
|
||||||
|
// Save Addition DateTime
|
||||||
|
TorrentsStartTime[hash] = QDateTime::currentDateTime();
|
||||||
|
TorrentsStartData[hash] = h.total_payload_download();
|
||||||
}
|
}
|
||||||
emit torrentFinishedChecking(hash);
|
emit torrentFinishedChecking(hash);
|
||||||
}
|
}
|
||||||
@@ -1051,32 +1296,16 @@ void bittorrent::readAlerts() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QPair<QString, QString> > bittorrent::getTrackersErrors(QString hash) const{
|
QHash<QString, QString> bittorrent::getTrackersErrors(QString hash) const{
|
||||||
return trackersErrors.value(hash, QList<QPair<QString, QString> >());
|
return trackersErrors.value(hash, QHash<QString, QString>());
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList bittorrent::getTorrentsToPauseAfterChecking() const{
|
QStringList bittorrent::getTorrentsToPauseAfterChecking() const{
|
||||||
return torrentsToPauseAfterChecking;
|
return torrentsToPauseAfterChecking;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to reload the torrent async after the torrent is actually
|
|
||||||
// paused so that we can get fastresume data
|
|
||||||
void bittorrent::pauseAndReloadTorrent(QTorrentHandle h) {
|
|
||||||
if(!h.is_valid()) {
|
|
||||||
std::cerr << "/!\\ Error: Invalid handle\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// ask to pause the torrent (async)
|
|
||||||
h.pause();
|
|
||||||
QString hash = h.hash();
|
|
||||||
// Add it to reloadingTorrents list so that we now we
|
|
||||||
// we should reload the torrent once we receive the
|
|
||||||
// torrent_paused_alert. pause() is async now...
|
|
||||||
reloadingTorrents << hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reload a torrent with full allocation mode
|
// Reload a torrent with full allocation mode
|
||||||
void bittorrent::reloadTorrent(const QTorrentHandle &h) {
|
void bittorrent::reloadTorrent(const QTorrentHandle &h, bool full_alloc) {
|
||||||
qDebug("** Reloading a torrent");
|
qDebug("** Reloading a torrent");
|
||||||
if(!h.is_valid()) {
|
if(!h.is_valid()) {
|
||||||
qDebug("/!\\ Error: Invalid handle");
|
qDebug("/!\\ Error: Invalid handle");
|
||||||
@@ -1086,7 +1315,7 @@ void bittorrent::reloadTorrent(const QTorrentHandle &h) {
|
|||||||
fs::path saveDir = h.save_path_boost();
|
fs::path saveDir = h.save_path_boost();
|
||||||
QString fileName = h.name();
|
QString fileName = h.name();
|
||||||
QString hash = h.hash();
|
QString hash = h.hash();
|
||||||
torrent_info t = h.get_torrent_info();
|
boost::intrusive_ptr<torrent_info> t(new torrent_info(h.get_torrent_info()));
|
||||||
qDebug("Reloading torrent: %s", fileName.toUtf8().data());
|
qDebug("Reloading torrent: %s", fileName.toUtf8().data());
|
||||||
entry resumeData;
|
entry resumeData;
|
||||||
// Checking if torrentBackup Dir exists
|
// Checking if torrentBackup Dir exists
|
||||||
@@ -1104,13 +1333,22 @@ void bittorrent::reloadTorrent(const QTorrentHandle &h) {
|
|||||||
// Add torrent again to session
|
// Add torrent again to session
|
||||||
unsigned int timeout = 0;
|
unsigned int timeout = 0;
|
||||||
while(h.is_valid() && timeout < 6) {
|
while(h.is_valid() && timeout < 6) {
|
||||||
|
qDebug("Waiting for the torrent to be removed...");
|
||||||
SleeperThread::msleep(1000);
|
SleeperThread::msleep(1000);
|
||||||
++timeout;
|
++timeout;
|
||||||
}
|
}
|
||||||
QTorrentHandle new_h = s->add_torrent(t, saveDir, resumeData, false);
|
QTorrentHandle new_h;
|
||||||
|
if(full_alloc) {
|
||||||
|
new_h = s->add_torrent(t, saveDir, resumeData, storage_mode_allocate);
|
||||||
qDebug("Using full allocation mode");
|
qDebug("Using full allocation mode");
|
||||||
|
} else {
|
||||||
new_h.set_max_uploads(-1);
|
new_h = s->add_torrent(t, saveDir, resumeData, storage_mode_sparse);
|
||||||
|
qDebug("Using sparse mode");
|
||||||
|
}
|
||||||
|
// Connections limit per torrent
|
||||||
|
new_h.set_max_connections(maxConnecsPerTorrent);
|
||||||
|
// Uploads limit per torrent
|
||||||
|
new_h.set_max_uploads(maxUploadsPerTorrent);
|
||||||
// Load filtered Files
|
// Load filtered Files
|
||||||
loadFilesPriorities(new_h);
|
loadFilesPriorities(new_h);
|
||||||
// Load speed limit from hard drive
|
// Load speed limit from hard drive
|
||||||
@@ -1151,6 +1389,7 @@ QString bittorrent::getSavePath(QString hash) {
|
|||||||
savePath = QString::fromUtf8(line.data());
|
savePath = QString::fromUtf8(line.data());
|
||||||
}else{
|
}else{
|
||||||
// use default save path
|
// use default save path
|
||||||
|
qDebug("Using default save path because none was set");
|
||||||
savePath = defaultSavePath;
|
savePath = defaultSavePath;
|
||||||
}
|
}
|
||||||
// Checking if savePath Dir exists
|
// Checking if savePath Dir exists
|
||||||
@@ -1226,7 +1465,7 @@ void bittorrent::applyEncryptionSettings(pe_settings se) {
|
|||||||
s->set_pe_settings(se);
|
s->set_pe_settings(se);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will fast resume unfinished torrents in
|
// Will fast resume torrents in
|
||||||
// backup directory
|
// backup directory
|
||||||
void bittorrent::resumeUnfinishedTorrents() {
|
void bittorrent::resumeUnfinishedTorrents() {
|
||||||
qDebug("Resuming unfinished torrents");
|
qDebug("Resuming unfinished torrents");
|
||||||
@@ -1242,7 +1481,7 @@ void bittorrent::resumeUnfinishedTorrents() {
|
|||||||
}
|
}
|
||||||
// Resume downloads
|
// Resume downloads
|
||||||
foreach(fileName, filePaths) {
|
foreach(fileName, filePaths) {
|
||||||
addTorrent(fileName, false);
|
addTorrent(fileName, false, QString(), true);
|
||||||
}
|
}
|
||||||
qDebug("Unfinished torrents resumed");
|
qDebug("Unfinished torrents resumed");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,10 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
#include <libtorrent/session.hpp>
|
#include <libtorrent/session.hpp>
|
||||||
|
#include <libtorrent/ip_filter.hpp>
|
||||||
#include "qtorrenthandle.h"
|
#include "qtorrenthandle.h"
|
||||||
|
|
||||||
using namespace libtorrent;
|
using namespace libtorrent;
|
||||||
@@ -34,6 +36,7 @@ using namespace libtorrent;
|
|||||||
class downloadThread;
|
class downloadThread;
|
||||||
class deleteThread;
|
class deleteThread;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
class FilterParserThread;
|
||||||
|
|
||||||
class bittorrent : public QObject{
|
class bittorrent : public QObject{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -43,20 +46,30 @@ class bittorrent : public QObject{
|
|||||||
QString scan_dir;
|
QString scan_dir;
|
||||||
QTimer *timerScan;
|
QTimer *timerScan;
|
||||||
QTimer *timerAlerts;
|
QTimer *timerAlerts;
|
||||||
|
QTimer *fastResumeSaver;
|
||||||
|
QTimer *BigRatioTimer;
|
||||||
bool DHTEnabled;
|
bool DHTEnabled;
|
||||||
downloadThread *downloader;
|
downloadThread *downloader;
|
||||||
QString defaultSavePath;
|
QString defaultSavePath;
|
||||||
QStringList torrentsToPauseAfterChecking;
|
QStringList torrentsToPauseAfterChecking;
|
||||||
QStringList reloadingTorrents;
|
QHash<QString, QDateTime> TorrentsStartTime;
|
||||||
QHash<QString, QList<qlonglong> > ETAstats;
|
QHash<QString, size_type> TorrentsStartData;
|
||||||
QHash<QString, qlonglong> ETAs;
|
|
||||||
QHash<QString, QPair<size_type,size_type> > ratioData;
|
QHash<QString, QPair<size_type,size_type> > ratioData;
|
||||||
QTimer *ETARefresher;
|
QHash<QString, QHash<QString, QString> > trackersErrors;
|
||||||
QHash<QString, QList<QPair<QString, QString> > > trackersErrors;
|
|
||||||
deleteThread *deleter;
|
deleteThread *deleter;
|
||||||
QStringList waitingForPause;
|
|
||||||
QStringList finishedTorrents;
|
QStringList finishedTorrents;
|
||||||
QStringList unfinishedTorrents;
|
QStringList unfinishedTorrents;
|
||||||
|
bool preAllocateAll;
|
||||||
|
bool addInPause;
|
||||||
|
int maxConnecsPerTorrent;
|
||||||
|
int maxUploadsPerTorrent;
|
||||||
|
float max_ratio;
|
||||||
|
bool UPnPEnabled;
|
||||||
|
bool NATPMPEnabled;
|
||||||
|
bool LSDEnabled;
|
||||||
|
FilterParserThread *filterParser;
|
||||||
|
QString filterPath;
|
||||||
|
int folderScanInterval; // in seconds
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString getSavePath(QString hash);
|
QString getSavePath(QString hash);
|
||||||
@@ -74,34 +87,36 @@ class bittorrent : public QObject{
|
|||||||
session_status getSessionStatus() const;
|
session_status getSessionStatus() const;
|
||||||
int getListenPort() const;
|
int getListenPort() const;
|
||||||
QStringList getTorrentsToPauseAfterChecking() const;
|
QStringList getTorrentsToPauseAfterChecking() const;
|
||||||
long getETA(QString hash) const;
|
qlonglong getETA(QString hash) const;
|
||||||
float getRealRatio(QString hash) const;
|
float getRealRatio(QString hash) const;
|
||||||
session* getSession() const;
|
session* getSession() const;
|
||||||
QList<QPair<QString, QString> > getTrackersErrors(QString hash) const;
|
QHash<QString, QString> getTrackersErrors(QString hash) const;
|
||||||
QStringList getFinishedTorrents() const;
|
QStringList getFinishedTorrents() const;
|
||||||
QStringList getUnfinishedTorrents() const;
|
QStringList getUnfinishedTorrents() const;
|
||||||
bool isFinished(QString hash) const;
|
bool isFinished(QString hash) const;
|
||||||
bool has_filtered_files(QString hash) const;
|
bool has_filtered_files(QString hash) const;
|
||||||
|
unsigned int getFinishedPausedTorrentsNb() const;
|
||||||
|
unsigned int getUnfinishedPausedTorrentsNb() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void addTorrent(QString path, bool fromScanDir = false, QString from_url = QString());
|
void addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false);
|
||||||
void downloadFromUrl(QString url);
|
void downloadFromUrl(QString url);
|
||||||
void downloadFromURLList(const QStringList& url_list);
|
void downloadFromURLList(const QStringList& url_list);
|
||||||
void deleteTorrent(QString hash, bool permanent = false);
|
void deleteTorrent(QString hash, bool permanent = false);
|
||||||
bool pauseTorrent(QString hash);
|
bool pauseTorrent(QString hash);
|
||||||
bool resumeTorrent(QString hash);
|
bool resumeTorrent(QString hash);
|
||||||
void enableDHT();
|
void pauseAllTorrents();
|
||||||
void disableDHT();
|
void resumeAllTorrents();
|
||||||
void saveDHTEntry();
|
void saveDHTEntry();
|
||||||
|
void preAllocateAllFiles(bool b);
|
||||||
void saveFastResumeAndRatioData();
|
void saveFastResumeAndRatioData();
|
||||||
|
void saveFastResumeAndRatioData(QString hash);
|
||||||
void enableDirectoryScanning(QString scan_dir);
|
void enableDirectoryScanning(QString scan_dir);
|
||||||
void disableDirectoryScanning();
|
void disableDirectoryScanning();
|
||||||
void enablePeerExchange();
|
void enablePeerExchange();
|
||||||
void enableIPFilter(ip_filter filter);
|
void enableIPFilter(QString filter);
|
||||||
void disableIPFilter();
|
void disableIPFilter();
|
||||||
void pauseAndReloadTorrent(QTorrentHandle h);
|
|
||||||
void resumeUnfinishedTorrents();
|
void resumeUnfinishedTorrents();
|
||||||
void updateETAs();
|
|
||||||
void saveTorrentSpeedLimits(QString hash);
|
void saveTorrentSpeedLimits(QString hash);
|
||||||
void loadTorrentSpeedLimits(QString hash);
|
void loadTorrentSpeedLimits(QString hash);
|
||||||
void saveDownloadUploadForTorrent(QString hash);
|
void saveDownloadUploadForTorrent(QString hash);
|
||||||
@@ -111,12 +126,16 @@ class bittorrent : public QObject{
|
|||||||
// Session configuration - Setters
|
// Session configuration - Setters
|
||||||
void setListeningPortsRange(std::pair<unsigned short, unsigned short> ports);
|
void setListeningPortsRange(std::pair<unsigned short, unsigned short> ports);
|
||||||
void setMaxConnections(int maxConnec);
|
void setMaxConnections(int maxConnec);
|
||||||
|
void setMaxConnectionsPerTorrent(int max);
|
||||||
|
void setMaxUploadsPerTorrent(int max);
|
||||||
void setDownloadRateLimit(long rate);
|
void setDownloadRateLimit(long rate);
|
||||||
void setUploadRateLimit(long rate);
|
void setUploadRateLimit(long rate);
|
||||||
void setGlobalRatio(float ratio);
|
void setGlobalRatio(float ratio);
|
||||||
|
void setDeleteRatio(float ratio);
|
||||||
void setDHTPort(int dht_port);
|
void setDHTPort(int dht_port);
|
||||||
void setProxySettings(proxy_settings proxySettings, bool trackers=true, bool peers=true, bool web_seeds=true, bool dht=true);
|
void setProxySettings(proxy_settings proxySettings, bool trackers=true, bool peers=true, bool web_seeds=true, bool dht=true);
|
||||||
void setSessionSettings(session_settings sessionSettings);
|
void setSessionSettings(session_settings sessionSettings);
|
||||||
|
void startTorrentsInPause(bool b);
|
||||||
void setDefaultSavePath(QString savepath);
|
void setDefaultSavePath(QString savepath);
|
||||||
void applyEncryptionSettings(pe_settings se);
|
void applyEncryptionSettings(pe_settings se);
|
||||||
void loadFilesPriorities(QTorrentHandle& h);
|
void loadFilesPriorities(QTorrentHandle& h);
|
||||||
@@ -124,6 +143,12 @@ class bittorrent : public QObject{
|
|||||||
void setUploadLimit(QString hash, long val);
|
void setUploadLimit(QString hash, long val);
|
||||||
void setUnfinishedTorrent(QString hash);
|
void setUnfinishedTorrent(QString hash);
|
||||||
void setFinishedTorrent(QString hash);
|
void setFinishedTorrent(QString hash);
|
||||||
|
void enableUPnP(bool b);
|
||||||
|
void enableNATPMP(bool b);
|
||||||
|
void enableLSD(bool b);
|
||||||
|
bool enableDHT(bool b);
|
||||||
|
void reloadTorrent(const QTorrentHandle &h, bool full_alloc);
|
||||||
|
void setTimerScanInterval(int secs);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void scanDirectory();
|
void scanDirectory();
|
||||||
@@ -131,12 +156,15 @@ class bittorrent : public QObject{
|
|||||||
void processDownloadedFile(QString, QString);
|
void processDownloadedFile(QString, QString);
|
||||||
bool loadTrackerFile(QString hash);
|
bool loadTrackerFile(QString hash);
|
||||||
void saveTrackerFile(QString hash);
|
void saveTrackerFile(QString hash);
|
||||||
void reloadTorrent(const QTorrentHandle &h); // This is protected now, call pauseAndReloadTorrent() instead
|
void deleteBigRatios();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void invalidTorrent(QString path);
|
void invalidTorrent(QString path);
|
||||||
void duplicateTorrent(QString path);
|
void duplicateTorrent(QString path);
|
||||||
void addedTorrent(QString path, QTorrentHandle& h, bool fastResume);
|
void addedTorrent(QString path, QTorrentHandle& h, bool fastResume);
|
||||||
|
void deletedTorrent(QString hash);
|
||||||
|
void pausedTorrent(QString hash);
|
||||||
|
void resumedTorrent(QString hash);
|
||||||
void finishedTorrent(QTorrentHandle& h);
|
void finishedTorrent(QTorrentHandle& h);
|
||||||
void fullDiskError(QTorrentHandle& h);
|
void fullDiskError(QTorrentHandle& h);
|
||||||
void trackerError(QString hash, QString time, QString msg);
|
void trackerError(QString hash, QString time, QString msg);
|
||||||
@@ -151,7 +179,9 @@ class bittorrent : public QObject{
|
|||||||
void fastResumeDataRejected(QString name);
|
void fastResumeDataRejected(QString name);
|
||||||
void urlSeedProblem(QString url, QString msg);
|
void urlSeedProblem(QString url, QString msg);
|
||||||
void torrentFinishedChecking(QString hash);
|
void torrentFinishedChecking(QString hash);
|
||||||
|
void torrent_ratio_deleted(QString fileName);
|
||||||
|
void UPnPError(QString msg);
|
||||||
|
void UPnPSuccess(QString msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,22 +5,16 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>605</width>
|
<width>592</width>
|
||||||
<height>588</height>
|
<height>655</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle" >
|
<property name="windowTitle" >
|
||||||
<string>Torrent Creation Tool</string>
|
<string>Torrent Creation Tool</string>
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
<widget class="QLabel" name="createTorrent_title" >
|
<widget class="QLabel" name="createTorrent_title" >
|
||||||
<property name="geometry" >
|
|
||||||
<rect>
|
|
||||||
<x>9</x>
|
|
||||||
<y>9</y>
|
|
||||||
<width>587</width>
|
|
||||||
<height>27</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize" >
|
<property name="minimumSize" >
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
@@ -51,49 +45,45 @@
|
|||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignCenter</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="layoutWidget" >
|
</item>
|
||||||
<property name="geometry" >
|
|
||||||
<rect>
|
|
||||||
<x>9</x>
|
|
||||||
<y>42</y>
|
|
||||||
<width>597</width>
|
|
||||||
<height>438</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" >
|
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" >
|
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="lbl_input" >
|
<widget class="QLabel" name="lbl_input" >
|
||||||
<property name="minimumSize" >
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>101</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize" >
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>26</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>Input files or directories:</string>
|
<string>File or folder to add to the torrent:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="textInputPath" />
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="addFile_button" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Add a file</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="icons.qrc" >:/Icons/add_file.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="addFolder_button" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Add a folder</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="icons.qrc" >:/Icons/add_folder.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="lbl_announce_url" >
|
<widget class="QLabel" name="lbl_announce_url" >
|
||||||
<property name="minimumSize" >
|
<property name="minimumSize" >
|
||||||
@@ -155,129 +145,22 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" >
|
<layout class="QVBoxLayout" >
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" >
|
<layout class="QHBoxLayout" >
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<property name="leftMargin" >
|
||||||
<widget class="QListWidget" name="input_list" >
|
|
||||||
<property name="selectionMode" >
|
|
||||||
<enum>QAbstractItemView::MultiSelection</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" >
|
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="spacing" >
|
<property name="topMargin" >
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<spacer>
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" >
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>16</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="addFolder_button" >
|
|
||||||
<property name="minimumSize" >
|
|
||||||
<size>
|
|
||||||
<width>22</width>
|
|
||||||
<height>22</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize" >
|
|
||||||
<size>
|
|
||||||
<width>22</width>
|
|
||||||
<height>22</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="removeFolder_button" >
|
|
||||||
<property name="minimumSize" >
|
|
||||||
<size>
|
|
||||||
<width>22</width>
|
|
||||||
<height>22</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize" >
|
|
||||||
<size>
|
|
||||||
<width>22</width>
|
|
||||||
<height>22</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="addFile_button" >
|
|
||||||
<property name="minimumSize" >
|
|
||||||
<size>
|
|
||||||
<width>22</width>
|
|
||||||
<height>22</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize" >
|
|
||||||
<size>
|
|
||||||
<width>22</width>
|
|
||||||
<height>22</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer>
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" >
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>16</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" >
|
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="spacing" >
|
<property name="rightMargin" >
|
||||||
<number>6</number>
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin" >
|
||||||
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QListWidget" name="trackers_list" >
|
<widget class="QListWidget" name="trackers_list" >
|
||||||
@@ -288,12 +171,21 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" >
|
<layout class="QVBoxLayout" >
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="leftMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<spacer>
|
<spacer>
|
||||||
<property name="orientation" >
|
<property name="orientation" >
|
||||||
@@ -324,6 +216,9 @@
|
|||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="icons.qrc" >:/Icons/skin/add.png</iconset>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@@ -343,6 +238,9 @@
|
|||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="icons.qrc" >:/Icons/skin/remove.png</iconset>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@@ -364,12 +262,21 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" >
|
<layout class="QHBoxLayout" >
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="leftMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QListWidget" name="URLSeeds_list" >
|
<widget class="QListWidget" name="URLSeeds_list" >
|
||||||
<property name="selectionMode" >
|
<property name="selectionMode" >
|
||||||
@@ -379,12 +286,21 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" >
|
<layout class="QVBoxLayout" >
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="leftMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<spacer>
|
<spacer>
|
||||||
<property name="orientation" >
|
<property name="orientation" >
|
||||||
@@ -415,6 +331,9 @@
|
|||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="icons.qrc" >:/Icons/skin/add.png</iconset>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@@ -434,6 +353,9 @@
|
|||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="icons.qrc" >:/Icons/skin/remove.png</iconset>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@@ -455,12 +377,6 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTextEdit" name="txt_comment" >
|
<widget class="QTextEdit" name="txt_comment" >
|
||||||
<property name="minimumSize" >
|
|
||||||
<size>
|
|
||||||
<width>421</width>
|
|
||||||
<height>102</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize" >
|
<property name="maximumSize" >
|
||||||
<size>
|
<size>
|
||||||
<width>421</width>
|
<width>421</width>
|
||||||
@@ -475,89 +391,122 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</item>
|
||||||
<widget class="QCheckBox" name="check_private" >
|
<item>
|
||||||
<property name="geometry" >
|
|
||||||
<rect>
|
|
||||||
<x>9</x>
|
|
||||||
<y>482</y>
|
|
||||||
<width>587</width>
|
|
||||||
<height>22</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="text" >
|
|
||||||
<string>Private (won't be distributed on trackerless network / DHT if enabled)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="layoutWidget2" >
|
|
||||||
<property name="geometry" >
|
|
||||||
<rect>
|
|
||||||
<x>9</x>
|
|
||||||
<y>510</y>
|
|
||||||
<width>587</width>
|
|
||||||
<height>30</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" >
|
<layout class="QHBoxLayout" >
|
||||||
<property name="margin" >
|
<item>
|
||||||
<number>0</number>
|
<widget class="QLabel" name="txtPieceSize" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Piece size:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="spacing" >
|
</widget>
|
||||||
<number>6</number>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboPieceSize" >
|
||||||
|
<property name="currentIndex" >
|
||||||
|
<number>3</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="lbl_destination" >
|
<property name="text" >
|
||||||
<property name="maximumSize" >
|
<string>32 KiB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text" >
|
||||||
|
<string>64 KiB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text" >
|
||||||
|
<string>128 KiB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text" >
|
||||||
|
<string>256 KiB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text" >
|
||||||
|
<string>512 KiB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text" >
|
||||||
|
<string>1 MiB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text" >
|
||||||
|
<string>2 MiB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text" >
|
||||||
|
<string>4 MiB</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" >
|
||||||
<size>
|
<size>
|
||||||
<width>16777215</width>
|
<width>40</width>
|
||||||
<height>26</height>
|
<height>20</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="check_private" >
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>Destination torrent file:</string>
|
<string>Private (won't be distributed on DHT network if enabled)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy" >
|
</widget>
|
||||||
<cstring>txt_destination</cstring>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkStartSeeding" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Start seeding after creation</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="progressLbl" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Progress:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QProgressBar" name="progressBar" >
|
||||||
|
<property name="value" >
|
||||||
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" >
|
<layout class="QHBoxLayout" >
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing" >
|
<property name="spacing" >
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<property name="leftMargin" >
|
||||||
<widget class="QLineEdit" name="txt_destination" />
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QToolButton" name="browse_destination" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="layoutWidget3" >
|
|
||||||
<property name="geometry" >
|
|
||||||
<rect>
|
|
||||||
<x>9</x>
|
|
||||||
<y>546</y>
|
|
||||||
<width>587</width>
|
|
||||||
<height>33</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" >
|
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="spacing" >
|
<property name="topMargin" >
|
||||||
<number>6</number>
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin" >
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin" >
|
||||||
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<spacer>
|
<spacer>
|
||||||
@@ -575,7 +524,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="createButton" >
|
<widget class="QPushButton" name="createButton" >
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>Create</string>
|
<string>Create and save...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -600,9 +549,12 @@
|
|||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
<resources>
|
||||||
<resources/>
|
<include location="icons.qrc" />
|
||||||
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
<sender>cancelButton</sender>
|
<sender>cancelButton</sender>
|
||||||
|
|||||||
@@ -36,57 +36,38 @@
|
|||||||
#include <libtorrent/file_pool.hpp>
|
#include <libtorrent/file_pool.hpp>
|
||||||
|
|
||||||
#include "createtorrent_imp.h"
|
#include "createtorrent_imp.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
using namespace libtorrent;
|
using namespace libtorrent;
|
||||||
using namespace boost::filesystem;
|
using namespace boost::filesystem;
|
||||||
|
|
||||||
createtorrent::createtorrent(QWidget *parent): QDialog(parent){
|
createtorrent::createtorrent(QWidget *parent): QDialog(parent){
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
addTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/add.png")));
|
|
||||||
removeTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
|
|
||||||
addURLSeed_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/add.png")));
|
|
||||||
removeURLSeed_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
|
|
||||||
removeFolder_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
|
|
||||||
addFolder_button->setIcon(QIcon(QString::fromUtf8(":/Icons/add_folder.png")));
|
|
||||||
addFile_button->setIcon(QIcon(QString::fromUtf8(":/Icons/add_file.png")));
|
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
creatorThread = new torrentCreatorThread();
|
||||||
|
connect(creatorThread, SIGNAL(creationSuccess(QString)), this, SLOT(handleCreationSucess(QString)));
|
||||||
|
connect(creatorThread, SIGNAL(creationFailure(QString)), this, SLOT(handleCreationFailure(QString)));
|
||||||
|
connect(creatorThread, SIGNAL(updateProgress(int)), this, SLOT(updateProgressBar(int)));
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::on_browse_destination_clicked(){
|
createtorrent::~createtorrent() {
|
||||||
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
|
delete creatorThread;
|
||||||
if(!destination.isEmpty()){
|
|
||||||
if(!destination.endsWith(QString::fromUtf8(".torrent")))
|
|
||||||
destination += QString::fromUtf8(".torrent");
|
|
||||||
txt_destination->setText(destination);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::on_addFolder_button_clicked(){
|
void createtorrent::on_addFolder_button_clicked(){
|
||||||
QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), QDir::homePath(), QFileDialog::ShowDirsOnly);
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), QDir::homePath(), QFileDialog::ShowDirsOnly);
|
||||||
if(!dir.isEmpty() && input_list->findItems(dir, Qt::MatchCaseSensitive).size() == 0)
|
if(!dir.isEmpty())
|
||||||
input_list->addItem(dir);
|
textInputPath->setText(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::on_addFile_button_clicked(){
|
void createtorrent::on_addFile_button_clicked(){
|
||||||
QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files to add to the torrent"), QDir::homePath(), QString(), 0, QFileDialog::ShowDirsOnly);
|
QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), QDir::homePath(), QString(), 0, QFileDialog::ShowDirsOnly);
|
||||||
QString file;
|
if(!file.isEmpty())
|
||||||
foreach(file, files){
|
textInputPath->setText(file);
|
||||||
if(input_list->findItems(file, Qt::MatchCaseSensitive).size() == 0){
|
|
||||||
input_list->addItem(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::on_removeFolder_button_clicked(){
|
void createtorrent::on_removeTracker_button_clicked() {
|
||||||
QModelIndexList selectedIndexes = input_list->selectionModel()->selectedIndexes();
|
|
||||||
for(int i=selectedIndexes.size()-1; i>=0; --i){
|
|
||||||
QListWidgetItem *item = input_list->takeItem(selectedIndexes.at(i).row());
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void createtorrent::on_removeTracker_button_clicked(){
|
|
||||||
QModelIndexList selectedIndexes = trackers_list->selectionModel()->selectedIndexes();
|
QModelIndexList selectedIndexes = trackers_list->selectionModel()->selectedIndexes();
|
||||||
for(int i=selectedIndexes.size()-1; i>=0; --i){
|
for(int i=selectedIndexes.size()-1; i>=0; --i){
|
||||||
QListWidgetItem *item = trackers_list->takeItem(selectedIndexes.at(i).row());
|
QListWidgetItem *item = trackers_list->takeItem(selectedIndexes.at(i).row());
|
||||||
@@ -94,7 +75,28 @@ void createtorrent::on_removeTracker_button_clicked(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::on_addTracker_button_clicked(){
|
int createtorrent::getPieceSize() const {
|
||||||
|
switch(comboPieceSize->currentIndex()) {
|
||||||
|
case 0:
|
||||||
|
return 32*1024;
|
||||||
|
case 1:
|
||||||
|
return 64*1024;
|
||||||
|
case 2:
|
||||||
|
return 128*1024;
|
||||||
|
case 3:
|
||||||
|
return 256*1024;
|
||||||
|
case 4:
|
||||||
|
return 512*1024;
|
||||||
|
case 5:
|
||||||
|
return 1024*1024;
|
||||||
|
case 6:
|
||||||
|
return 2048*1024;
|
||||||
|
default:
|
||||||
|
return 256*1024;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void createtorrent::on_addTracker_button_clicked() {
|
||||||
bool ok;
|
bool ok;
|
||||||
QString URL = QInputDialog::getText(this, tr("Please type an announce URL"),
|
QString URL = QInputDialog::getText(this, tr("Please type an announce URL"),
|
||||||
tr("Announce URL:", "Tracker URL"), QLineEdit::Normal,
|
tr("Announce URL:", "Tracker URL"), QLineEdit::Normal,
|
||||||
@@ -127,11 +129,13 @@ void createtorrent::on_addURLSeed_button_clicked(){
|
|||||||
// Subfunction to add files to a torrent_info structure
|
// Subfunction to add files to a torrent_info structure
|
||||||
// Written by Arvid Norberg (libtorrent Author)
|
// Written by Arvid Norberg (libtorrent Author)
|
||||||
void add_files(torrent_info& t, path const& p, path const& l){
|
void add_files(torrent_info& t, path const& p, path const& l){
|
||||||
|
qDebug("p: %s, l: %s, l.leaf(): %s", p.string().c_str(), l.string().c_str(), l.leaf().c_str());
|
||||||
path f(p / l);
|
path f(p / l);
|
||||||
if (is_directory(f)){
|
if (is_directory(f)){
|
||||||
for (directory_iterator i(f), end; i != end; ++i)
|
for (directory_iterator i(f), end; i != end; ++i)
|
||||||
add_files(t, p, l / i->leaf());
|
add_files(t, p, l / i->leaf());
|
||||||
}else{
|
}else{
|
||||||
|
qDebug("Adding %s", l.string().c_str());
|
||||||
t.add_file(l, file_size(f));
|
t.add_file(l, file_size(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,67 +151,118 @@ QStringList createtorrent::allItems(QListWidget *list){
|
|||||||
|
|
||||||
// Main function that create a .torrent file
|
// Main function that create a .torrent file
|
||||||
void createtorrent::on_createButton_clicked(){
|
void createtorrent::on_createButton_clicked(){
|
||||||
QString destination = txt_destination->text();
|
QString input = textInputPath->text().trimmed();
|
||||||
if(destination.isEmpty()){
|
if (input.endsWith(QDir::separator()))
|
||||||
QMessageBox::critical(0, tr("No destination path set"), tr("Please type a destination path first"));
|
input.chop(1);
|
||||||
return;
|
if(input.isEmpty()){
|
||||||
}
|
|
||||||
QStringList input = allItems(input_list);
|
|
||||||
if(input.size() == 0){
|
|
||||||
QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first"));
|
QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QStringList trackers = allItems(trackers_list);
|
||||||
|
if(!trackers.size()){
|
||||||
|
QMessageBox::critical(0, tr("No tracker path set"), tr("Please set at least one tracker"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
|
||||||
|
if(!destination.isEmpty()) {
|
||||||
|
if(!destination.endsWith(QString::fromUtf8(".torrent")))
|
||||||
|
destination += QString::fromUtf8(".torrent");
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QStringList url_seeds = allItems(URLSeeds_list);
|
||||||
|
QString comment = txt_comment->toPlainText();
|
||||||
|
creatorThread->create(input, destination, trackers, url_seeds, comment, check_private->isChecked(), getPieceSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void createtorrent::handleCreationFailure(QString msg) {
|
||||||
|
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent creation was unsuccessful, reason: %1").arg(msg));
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createtorrent::handleCreationSuccess(QString path, const char* branch_path, QString hash) {
|
||||||
|
if(checkStartSeeding->isChecked()) {
|
||||||
|
// Create save path file
|
||||||
|
QFile savepath_file(misc::qBittorrentPath()+QString::fromUtf8("BT_backup")+QDir::separator()+hash+QString::fromUtf8(".savepath"));
|
||||||
|
savepath_file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||||
|
savepath_file.write(branch_path);
|
||||||
|
savepath_file.close();
|
||||||
|
emit torrent_to_seed(path);
|
||||||
|
}
|
||||||
|
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+path);
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createtorrent::updateProgressBar(int progress) {
|
||||||
|
progressBar->setValue(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Torrent Creator Thread
|
||||||
|
//
|
||||||
|
|
||||||
|
void torrentCreatorThread::create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size) {
|
||||||
|
input_path = _input_path;
|
||||||
|
save_path = _save_path;
|
||||||
|
trackers = _trackers;
|
||||||
|
url_seeds = _url_seeds;
|
||||||
|
comment = _comment;
|
||||||
|
is_private = _is_private;
|
||||||
|
piece_size = _piece_size;
|
||||||
|
abort = false;
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void torrentCreatorThread::run() {
|
||||||
|
emit updateProgress(0);
|
||||||
char const* creator_str = "qBittorrent "VERSION;
|
char const* creator_str = "qBittorrent "VERSION;
|
||||||
try {
|
try {
|
||||||
torrent_info t;
|
boost::intrusive_ptr<torrent_info> t(new torrent_info);
|
||||||
QString input_path;
|
ofstream out(complete(path((const char*)save_path.toUtf8())), std::ios_base::binary);
|
||||||
path full_path;
|
// Adding files to the torrent
|
||||||
ofstream out(complete(path((const char*)destination.toUtf8())), std::ios_base::binary);
|
path full_path = complete(path(input_path.toUtf8().data()));
|
||||||
foreach(input_path, input){
|
add_files(*t, full_path.branch_path(), full_path.leaf());
|
||||||
full_path = complete(path((const char*)input_path.toUtf8()));
|
if(abort) return;
|
||||||
add_files(t, full_path.branch_path(), full_path.leaf());
|
// Set piece size
|
||||||
}
|
t->set_piece_size(piece_size);
|
||||||
int piece_size = 256 * 1024;
|
|
||||||
t.set_piece_size(piece_size);
|
|
||||||
// Add url seeds
|
// Add url seeds
|
||||||
QStringList urlSeeds = allItems(URLSeeds_list);
|
|
||||||
QString seed;
|
QString seed;
|
||||||
foreach(seed, urlSeeds){
|
foreach(seed, url_seeds){
|
||||||
t.add_url_seed((const char*)seed.toUtf8());
|
t->add_url_seed(seed.toUtf8().data());
|
||||||
}
|
}
|
||||||
QStringList trackers = allItems(trackers_list);
|
|
||||||
for(int i=0; i<trackers.size(); ++i){
|
for(int i=0; i<trackers.size(); ++i){
|
||||||
t.add_tracker((const char*)trackers.at(i).toUtf8());
|
t->add_tracker(trackers.at(i).toUtf8().data());
|
||||||
}
|
}
|
||||||
|
if(abort) return;
|
||||||
// calculate the hash for all pieces
|
// calculate the hash for all pieces
|
||||||
file_pool fp;
|
file_pool fp;
|
||||||
boost::scoped_ptr<storage_interface> st(default_storage_constructor(t, full_path.branch_path(), fp));
|
boost::scoped_ptr<storage_interface> st(default_storage_constructor(t, full_path.branch_path(), fp));
|
||||||
int num = t.num_pieces();
|
int num = t->num_pieces();
|
||||||
std::vector<char> buf(piece_size);
|
std::vector<char> buf(piece_size);
|
||||||
for (int i = 0; i < num; ++i) {
|
for (int i = 0; i < num; ++i) {
|
||||||
st->read(&buf[0], i, 0, t.piece_size(i));
|
st->read(&buf[0], i, 0, t->piece_size(i));
|
||||||
hasher h(&buf[0], t.piece_size(i));
|
hasher h(&buf[0], t->piece_size(i));
|
||||||
t.set_hash(i, h.final());
|
t->set_hash(i, h.final());
|
||||||
|
emit updateProgress((int)(i*100./(float)num));
|
||||||
|
if(abort) return;
|
||||||
}
|
}
|
||||||
// Set qBittorrent as creator and add user comment to
|
// Set qBittorrent as creator and add user comment to
|
||||||
// torrent_info structure
|
// torrent_info structure
|
||||||
t.set_creator(creator_str);
|
t->set_creator(creator_str);
|
||||||
t.set_comment((const char*)txt_comment->toPlainText().toUtf8());
|
t->set_comment((const char*)comment.toUtf8());
|
||||||
// Is private ?
|
// Is private ?
|
||||||
if(check_private->isChecked()){
|
if(is_private){
|
||||||
t.set_priv(true);
|
t->set_priv(true);
|
||||||
}
|
}
|
||||||
|
if(abort) return;
|
||||||
// create the torrent and print it to out
|
// create the torrent and print it to out
|
||||||
entry e = t.create_torrent();
|
entry e = t->create_torrent();
|
||||||
libtorrent::bencode(std::ostream_iterator<char>(out), e);
|
libtorrent::bencode(std::ostream_iterator<char>(out), e);
|
||||||
|
out.flush();
|
||||||
|
emit updateProgress(100);
|
||||||
|
emit creationSuccess(save_path, full_path.branch_path().string().c_str(), misc::toQString(t->info_hash()));
|
||||||
}
|
}
|
||||||
catch (std::exception& e){
|
catch (std::exception& e){
|
||||||
std::cerr << e.what() << "\n";
|
emit creationFailure(QString::fromUtf8(e.what()));
|
||||||
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent creation was unsuccessful, reason: %1").arg(QString::fromUtf8(e.what())));
|
|
||||||
hide();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
hide();
|
|
||||||
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+destination);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,25 +22,65 @@
|
|||||||
#ifndef CREATE_TORRENT_IMP_H
|
#ifndef CREATE_TORRENT_IMP_H
|
||||||
#define CREATE_TORRENT_IMP_H
|
#define CREATE_TORRENT_IMP_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
#include "ui_createtorrent.h"
|
#include "ui_createtorrent.h"
|
||||||
|
|
||||||
|
class torrentCreatorThread : public QThread {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
QString input_path;
|
||||||
|
QString save_path;
|
||||||
|
QStringList trackers;
|
||||||
|
QStringList url_seeds;
|
||||||
|
QString comment;
|
||||||
|
bool is_private;
|
||||||
|
int piece_size;
|
||||||
|
bool abort;
|
||||||
|
|
||||||
|
public:
|
||||||
|
torrentCreatorThread() {}
|
||||||
|
~torrentCreatorThread() {
|
||||||
|
abort = true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
void create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void creationFailure(QString msg);
|
||||||
|
void creationSuccess(QString path, const char* branch_path, QString hash);
|
||||||
|
void updateProgress(int progress);
|
||||||
|
};
|
||||||
|
|
||||||
class createtorrent : public QDialog, private Ui::createTorrentDialog{
|
class createtorrent : public QDialog, private Ui::createTorrentDialog{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
torrentCreatorThread *creatorThread;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
createtorrent(QWidget *parent = 0);
|
createtorrent(QWidget *parent = 0);
|
||||||
|
~createtorrent();
|
||||||
QStringList allItems(QListWidget *list);
|
QStringList allItems(QListWidget *list);
|
||||||
|
int getPieceSize() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void torrent_to_seed(QString path);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void on_browse_destination_clicked();
|
|
||||||
void on_createButton_clicked();
|
void on_createButton_clicked();
|
||||||
void on_addFile_button_clicked();
|
void on_addFile_button_clicked();
|
||||||
void on_addFolder_button_clicked();
|
void on_addFolder_button_clicked();
|
||||||
void on_removeFolder_button_clicked();
|
|
||||||
void on_addTracker_button_clicked();
|
void on_addTracker_button_clicked();
|
||||||
void on_removeTracker_button_clicked();
|
void on_removeTracker_button_clicked();
|
||||||
void on_addURLSeed_button_clicked();
|
void on_addURLSeed_button_clicked();
|
||||||
void on_removeURLSeed_button_clicked();
|
void on_removeURLSeed_button_clicked();
|
||||||
|
void handleCreationFailure(QString msg);
|
||||||
|
void handleCreationSuccess(QString path, const char* branch_path, QString hash);
|
||||||
|
void updateProgressBar(int progress);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -28,17 +28,17 @@
|
|||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
|
|
||||||
#include "misc.h"
|
#include "arborescence.h"
|
||||||
|
|
||||||
class subDeleteThread : public QThread {
|
class subDeleteThread : public QThread {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private:
|
private:
|
||||||
QString save_path;
|
QString save_path;
|
||||||
QStringList files_path;
|
arborescence *arb;
|
||||||
bool abort;
|
bool abort;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
subDeleteThread(QObject *parent, QString save_path, QStringList files_path) : QThread(parent), save_path(save_path), files_path(files_path), abort(false){}
|
subDeleteThread(QObject *parent, QString saveDir, arborescence *arb) : QThread(parent), save_path(saveDir), arb(arb), abort(false){}
|
||||||
|
|
||||||
~subDeleteThread(){
|
~subDeleteThread(){
|
||||||
abort = true;
|
abort = true;
|
||||||
@@ -52,10 +52,11 @@ class subDeleteThread : public QThread {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void run(){
|
void run(){
|
||||||
if(misc::removeTorrentSavePath(save_path, files_path))
|
if(arb->removeFromFS(save_path))
|
||||||
emit deletionSuccessST(this);
|
emit deletionSuccessST(this);
|
||||||
else
|
else
|
||||||
emit deletionFailureST(this);
|
emit deletionFailureST(this);
|
||||||
|
delete arb;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ class deleteThread : public QThread {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<QPair<QString, QStringList> > torrents_list;
|
QList<QPair<QString, arborescence*> > torrents_list;
|
||||||
QMutex mutex;
|
QMutex mutex;
|
||||||
QWaitCondition condition;
|
QWaitCondition condition;
|
||||||
bool abort;
|
bool abort;
|
||||||
@@ -81,9 +82,10 @@ class deleteThread : public QThread {
|
|||||||
wait();
|
wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteTorrent(QString save_path, QStringList files_path){
|
void deleteTorrent(QString saveDir, arborescence *arb){
|
||||||
|
qDebug("deleteThread called");
|
||||||
QMutexLocker locker(&mutex);
|
QMutexLocker locker(&mutex);
|
||||||
torrents_list << QPair<QString, QStringList>(save_path, files_path);
|
torrents_list << QPair<QString, arborescence*>(saveDir, arb);
|
||||||
if(!isRunning()){
|
if(!isRunning()){
|
||||||
start();
|
start();
|
||||||
}else{
|
}else{
|
||||||
@@ -98,7 +100,7 @@ class deleteThread : public QThread {
|
|||||||
return;
|
return;
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
if(torrents_list.size() != 0){
|
if(torrents_list.size() != 0){
|
||||||
QPair<QString, QStringList> torrent = torrents_list.takeFirst();
|
QPair<QString, arborescence *> torrent = torrents_list.takeFirst();
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
subDeleteThread *st = new subDeleteThread(0, torrent.first, torrent.second);
|
subDeleteThread *st = new subDeleteThread(0, torrent.first, torrent.second);
|
||||||
subThreads << st;
|
subThreads << st;
|
||||||
|
|||||||
263
src/download.ui
@@ -21,185 +21,6 @@
|
|||||||
<property name="margin" >
|
<property name="margin" >
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" >
|
|
||||||
<property name="spacing" >
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<spacer>
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" >
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="lbl_DLSpeed" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>Total DL Speed:</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment" >
|
|
||||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLCDNumber" name="LCD_DownSpeed" >
|
|
||||||
<property name="autoFillBackground" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="frameShadow" >
|
|
||||||
<enum>QFrame::Raised</enum>
|
|
||||||
</property>
|
|
||||||
<property name="smallDecimalPoint" >
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="numDigits" >
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="segmentStyle" >
|
|
||||||
<enum>QLCDNumber::Flat</enum>
|
|
||||||
</property>
|
|
||||||
<property name="value" stdset="0" >
|
|
||||||
<double>0.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
<property name="intValue" stdset="0" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="unitDL" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>KiB/s</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment" >
|
|
||||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer>
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" >
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>Session ratio: </string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment" >
|
|
||||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLCDNumber" name="LCD_Ratio" >
|
|
||||||
<property name="autoFillBackground" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="numDigits" >
|
|
||||||
<number>4</number>
|
|
||||||
</property>
|
|
||||||
<property name="segmentStyle" >
|
|
||||||
<enum>QLCDNumber::Flat</enum>
|
|
||||||
</property>
|
|
||||||
<property name="value" stdset="0" >
|
|
||||||
<double>1.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="lbl_ratio_icon" >
|
|
||||||
<property name="text" >
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer>
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" >
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="lbl_UPSpeed" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>Total UP Speed:</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment" >
|
|
||||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLCDNumber" name="LCD_UpSpeed" >
|
|
||||||
<property name="autoFillBackground" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="smallDecimalPoint" >
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="numDigits" >
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="segmentStyle" >
|
|
||||||
<enum>QLCDNumber::Flat</enum>
|
|
||||||
</property>
|
|
||||||
<property name="value" stdset="0" >
|
|
||||||
<double>0.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="unitUP" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>KiB/s</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment" >
|
|
||||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer>
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" >
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeView" name="downloadList" >
|
<widget class="QTreeView" name="downloadList" >
|
||||||
<property name="minimumSize" >
|
<property name="minimumSize" >
|
||||||
@@ -242,6 +63,14 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="log_tab" >
|
<widget class="QWidget" name="log_tab" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>765</width>
|
||||||
|
<height>138</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
<attribute name="title" >
|
<attribute name="title" >
|
||||||
<string>Log</string>
|
<string>Log</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
@@ -255,9 +84,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QTextBrowser" name="infoBar" >
|
<widget class="QTextBrowser" name="infoBar" >
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy" >
|
||||||
<sizepolicy>
|
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||||
<hsizetype>7</hsizetype>
|
|
||||||
<vsizetype>7</vsizetype>
|
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
@@ -276,6 +103,14 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="filter_tab" >
|
<widget class="QWidget" name="filter_tab" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>765</width>
|
||||||
|
<height>138</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
<attribute name="title" >
|
<attribute name="title" >
|
||||||
<string>IP filter</string>
|
<string>IP filter</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
@@ -346,7 +181,69 @@
|
|||||||
<string>Torrent Properties</string>
|
<string>Torrent Properties</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionOpen_destination_folder" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="icons.qrc" >
|
||||||
|
<normaloff>:/Icons/folder.png</normaloff>:/Icons/folder.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Open destination folder</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHOSColName" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Name</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHOSColSize" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Size</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHOSColProgress" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Progress</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHOSColDownSpeed" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>DLSpeed</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHOSColUpSpeed" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>UpSpeed</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHOSColSeedersLeechers" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Seeds/Leechs</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHOSColRatio" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Ratio</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionHOSColEta" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>ETA</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionBuy_it" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="icons.qrc" >
|
||||||
|
<normaloff>:/Icons/money.png</normaloff>:/Icons/money.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Buy it</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<zorder>tabBottom</zorder>
|
||||||
|
<zorder></zorder>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources>
|
||||||
|
<include location="icons.qrc" />
|
||||||
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|||||||
207
src/downloadThread.cpp
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "downloadThread.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// http://curl.rtin.bz/libcurl/c/libcurl-errors.html
|
||||||
|
QString subDownloadThread::errorCodeToString(CURLcode status) {
|
||||||
|
switch(status){
|
||||||
|
case CURLE_FTP_CANT_GET_HOST:
|
||||||
|
case CURLE_COULDNT_RESOLVE_HOST:
|
||||||
|
return tr("Host is unreachable");
|
||||||
|
case CURLE_READ_ERROR:
|
||||||
|
case CURLE_FILE_COULDNT_READ_FILE:
|
||||||
|
return tr("File was not found (404)");
|
||||||
|
case CURLE_FTP_ACCESS_DENIED:
|
||||||
|
case CURLE_LOGIN_DENIED:
|
||||||
|
case CURLE_FTP_USER_PASSWORD_INCORRECT:
|
||||||
|
return tr("Connection was denied");
|
||||||
|
case CURLE_URL_MALFORMAT:
|
||||||
|
return tr("Url is invalid");
|
||||||
|
case CURLE_COULDNT_RESOLVE_PROXY:
|
||||||
|
return tr("Could not resolve proxy");
|
||||||
|
//case 5:
|
||||||
|
// return tr("Connection forbidden (403)");
|
||||||
|
//case 6:
|
||||||
|
// return tr("Connection was not authorized (401)");
|
||||||
|
//case 7:
|
||||||
|
// return tr("Content has moved (301)");
|
||||||
|
case CURLE_COULDNT_CONNECT:
|
||||||
|
return tr("Connection failure");
|
||||||
|
case CURLE_OPERATION_TIMEOUTED:
|
||||||
|
return tr("Connection was timed out");
|
||||||
|
case CURLE_INTERFACE_FAILED:
|
||||||
|
return tr("Incorrect network interface");
|
||||||
|
default:
|
||||||
|
return tr("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subDownloadThread::subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){}
|
||||||
|
|
||||||
|
subDownloadThread::~subDownloadThread(){
|
||||||
|
abort = true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void subDownloadThread::run(){
|
||||||
|
// XXX: Trick to get a unique filename
|
||||||
|
QString filePath;
|
||||||
|
QTemporaryFile *tmpfile = new QTemporaryFile();
|
||||||
|
if (tmpfile->open()) {
|
||||||
|
filePath = tmpfile->fileName();
|
||||||
|
}
|
||||||
|
delete tmpfile;
|
||||||
|
FILE *f = fopen(filePath.toUtf8().data(), "wb");
|
||||||
|
if(!f) {
|
||||||
|
std::cerr << "couldn't open destination file" << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CURL *curl;
|
||||||
|
CURLcode res;
|
||||||
|
curl = curl_easy_init();
|
||||||
|
if(curl) {
|
||||||
|
std::string c_url = url.toUtf8().data();
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, c_url.c_str());
|
||||||
|
// SSL support
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||||
|
// PROXY SUPPORT
|
||||||
|
QSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
int intValue = settings.value(QString::fromUtf8("Preferences/Connection/ProxyType"), 0).toInt();
|
||||||
|
if(intValue > 0) {
|
||||||
|
// Proxy enabled
|
||||||
|
QString IP = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/IP"), "0.0.0.0").toString();
|
||||||
|
QString port = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Port"), 8080).toString();
|
||||||
|
qDebug("Using proxy: %s", (IP+QString(":")+port).toUtf8().data());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROXYPORT, (IP+QString(":")+port).toUtf8().data());
|
||||||
|
// Default proxy type is HTTP, we must change if it is SOCKS5
|
||||||
|
if(intValue%2==0) {
|
||||||
|
qDebug("Proxy is SOCKS5, not HTTP");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
|
||||||
|
}
|
||||||
|
// Authentication?
|
||||||
|
if(intValue > 2) {
|
||||||
|
qDebug("Proxy requires authentication, authenticating");
|
||||||
|
QString username = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Username"), QString()).toString();
|
||||||
|
QString password = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Password"), QString()).toString();
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, (username+QString(":")+password).toUtf8().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We have to define CURLOPT_WRITEFUNCTION or it will crash on windows
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
|
||||||
|
// Verbose
|
||||||
|
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
|
||||||
|
// No progress info (we don't use it)
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
|
||||||
|
// Redirections
|
||||||
|
curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, -1);
|
||||||
|
qDebug("Downloading %s", url.toUtf8().data());
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
/* always cleanup */
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
fclose(f);
|
||||||
|
if(res) {
|
||||||
|
emit downloadFailureST(this, url, errorCodeToString(res));
|
||||||
|
} else {
|
||||||
|
emit downloadFinishedST(this, url, filePath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "Could not initialize CURL" << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Download Thread **/
|
||||||
|
|
||||||
|
downloadThread::downloadThread(QObject* parent) : QThread(parent), abort(false){}
|
||||||
|
|
||||||
|
downloadThread::~downloadThread(){
|
||||||
|
mutex.lock();
|
||||||
|
abort = true;
|
||||||
|
condition.wakeOne();
|
||||||
|
mutex.unlock();
|
||||||
|
qDeleteAll(subThreads);
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void downloadThread::downloadUrl(QString url){
|
||||||
|
QMutexLocker locker(&mutex);
|
||||||
|
if(downloading_list.contains(url)) return;
|
||||||
|
url_list << url;
|
||||||
|
downloading_list << url;
|
||||||
|
if(!isRunning()){
|
||||||
|
start();
|
||||||
|
}else{
|
||||||
|
condition.wakeOne();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void downloadThread::run(){
|
||||||
|
forever{
|
||||||
|
if(abort)
|
||||||
|
return;
|
||||||
|
mutex.lock();
|
||||||
|
if(url_list.size() != 0){
|
||||||
|
QString url = url_list.takeFirst();
|
||||||
|
mutex.unlock();
|
||||||
|
subDownloadThread *st = new subDownloadThread(0, url);
|
||||||
|
subThreads << st;
|
||||||
|
connect(st, SIGNAL(downloadFinishedST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadedFile(subDownloadThread*, QString, QString)));
|
||||||
|
connect(st, SIGNAL(downloadFailureST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadFailure(subDownloadThread*, QString, QString)));
|
||||||
|
st->start();
|
||||||
|
}else{
|
||||||
|
condition.wait(&mutex);
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void downloadThread::propagateDownloadedFile(subDownloadThread* st, QString url, QString path){
|
||||||
|
int index = subThreads.indexOf(st);
|
||||||
|
Q_ASSERT(index != -1);
|
||||||
|
subThreads.removeAt(index);
|
||||||
|
delete st;
|
||||||
|
emit downloadFinished(url, path);
|
||||||
|
mutex.lock();
|
||||||
|
index = downloading_list.indexOf(url);
|
||||||
|
Q_ASSERT(index != -1);
|
||||||
|
downloading_list.removeAt(index);
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void downloadThread::propagateDownloadFailure(subDownloadThread* st, QString url, QString reason){
|
||||||
|
int index = subThreads.indexOf(st);
|
||||||
|
Q_ASSERT(index != -1);
|
||||||
|
subThreads.removeAt(index);
|
||||||
|
delete st;
|
||||||
|
emit downloadFailure(url, reason);
|
||||||
|
mutex.lock();
|
||||||
|
index = downloading_list.indexOf(url);
|
||||||
|
Q_ASSERT(index != -1);
|
||||||
|
downloading_list.removeAt(index);
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
@@ -29,55 +29,18 @@
|
|||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
#include <QWaitCondition>
|
#include <QWaitCondition>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <iostream>
|
#include <curl/curl.h>
|
||||||
#include <cc++/common.h>
|
|
||||||
|
|
||||||
#ifdef CCXX_NAMESPACES
|
|
||||||
using namespace std;
|
|
||||||
using namespace ost;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class subDownloadThread : public QThread {
|
class subDownloadThread : public QThread {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private:
|
private:
|
||||||
QString url;
|
QString url;
|
||||||
URLStream url_stream;
|
|
||||||
bool abort;
|
bool abort;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){}
|
subDownloadThread(QObject *parent, QString url);
|
||||||
|
~subDownloadThread();
|
||||||
~subDownloadThread(){
|
QString errorCodeToString(CURLcode status);
|
||||||
abort = true;
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString errorCodeToString(URLStream::Error status){
|
|
||||||
switch(status){
|
|
||||||
case URLStream::errUnreachable:
|
|
||||||
return tr("Host is unreachable");
|
|
||||||
case URLStream::errMissing:
|
|
||||||
return tr("File was not found (404)");
|
|
||||||
case URLStream::errDenied:
|
|
||||||
return tr("Connection was denied");
|
|
||||||
case URLStream::errInvalid:
|
|
||||||
return tr("Url is invalid");
|
|
||||||
case URLStream::errForbidden:
|
|
||||||
return tr("Connection forbidden (403)");
|
|
||||||
case URLStream::errUnauthorized:
|
|
||||||
return tr("Connection was not authorized (401)");
|
|
||||||
case URLStream::errRelocated:
|
|
||||||
return tr("Content has moved (301)");
|
|
||||||
case URLStream::errFailure:
|
|
||||||
return tr("Connection failure");
|
|
||||||
case URLStream::errTimeout:
|
|
||||||
return tr("Connection was timed out");
|
|
||||||
case URLStream::errInterface:
|
|
||||||
return tr("Incorrect network interface");
|
|
||||||
default:
|
|
||||||
return tr("Unknown error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// For subthreads
|
// For subthreads
|
||||||
@@ -85,47 +48,7 @@ class subDownloadThread : public QThread {
|
|||||||
void downloadFailureST(subDownloadThread* st, QString url, QString reason);
|
void downloadFailureST(subDownloadThread* st, QString url, QString reason);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void run(){
|
void run();
|
||||||
// XXX: Trick to get a unique filename
|
|
||||||
QString filePath;
|
|
||||||
QTemporaryFile *tmpfile = new QTemporaryFile();
|
|
||||||
if (tmpfile->open()) {
|
|
||||||
filePath = tmpfile->fileName();
|
|
||||||
}
|
|
||||||
delete tmpfile;
|
|
||||||
QFile dest_file(filePath);
|
|
||||||
if(!dest_file.open(QIODevice::WriteOnly | QIODevice::Text)){
|
|
||||||
std::cerr << "Error: could't create temporary file: " << (const char*)filePath.toUtf8() << '\n';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
URLStream::Error status = url_stream.get((const char*)url.toUtf8());
|
|
||||||
if(status){
|
|
||||||
// Failure
|
|
||||||
QString error_msg = errorCodeToString(status);
|
|
||||||
qDebug("Download failed for %s, reason: %s", (const char*)url.toUtf8(), (const char*)error_msg.toUtf8());
|
|
||||||
url_stream.close();
|
|
||||||
emit downloadFailureST(this, url, error_msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
qDebug("Downloading %s...", (const char*)url.toUtf8());
|
|
||||||
char cbuf[1024];
|
|
||||||
int len;
|
|
||||||
while(!url_stream.eof()) {
|
|
||||||
url_stream.read(cbuf, sizeof(cbuf));
|
|
||||||
len = url_stream.gcount();
|
|
||||||
if(len > 0)
|
|
||||||
dest_file.write(cbuf, len);
|
|
||||||
if(abort){
|
|
||||||
dest_file.close();
|
|
||||||
url_stream.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dest_file.close();
|
|
||||||
url_stream.close();
|
|
||||||
emit downloadFinishedST(this, url, filePath);
|
|
||||||
qDebug("download completed here: %s", (const char*)filePath.toUtf8());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class downloadThread : public QThread {
|
class downloadThread : public QThread {
|
||||||
@@ -144,75 +67,19 @@ class downloadThread : public QThread {
|
|||||||
void downloadFailure(QString url, QString reason);
|
void downloadFailure(QString url, QString reason);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
downloadThread(QObject* parent) : QThread(parent), abort(false){}
|
downloadThread(QObject* parent);
|
||||||
|
|
||||||
~downloadThread(){
|
~downloadThread();
|
||||||
mutex.lock();
|
|
||||||
abort = true;
|
|
||||||
condition.wakeOne();
|
|
||||||
mutex.unlock();
|
|
||||||
qDeleteAll(subThreads);
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void downloadUrl(QString url){
|
void downloadUrl(QString url);
|
||||||
QMutexLocker locker(&mutex);
|
void setProxy(QString IP, int port, QString username, QString password);
|
||||||
if(downloading_list.contains(url)) return;
|
|
||||||
url_list << url;
|
|
||||||
downloading_list << url;
|
|
||||||
if(!isRunning()){
|
|
||||||
start();
|
|
||||||
}else{
|
|
||||||
condition.wakeOne();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void run(){
|
void run();
|
||||||
forever{
|
|
||||||
if(abort)
|
|
||||||
return;
|
|
||||||
mutex.lock();
|
|
||||||
if(url_list.size() != 0){
|
|
||||||
QString url = url_list.takeFirst();
|
|
||||||
mutex.unlock();
|
|
||||||
subDownloadThread *st = new subDownloadThread(0, url);
|
|
||||||
subThreads << st;
|
|
||||||
connect(st, SIGNAL(downloadFinishedST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadedFile(subDownloadThread*, QString, QString)));
|
|
||||||
connect(st, SIGNAL(downloadFailureST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadFailure(subDownloadThread*, QString, QString)));
|
|
||||||
st->start();
|
|
||||||
}else{
|
|
||||||
condition.wait(&mutex);
|
|
||||||
mutex.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected slots:
|
|
||||||
void propagateDownloadedFile(subDownloadThread* st, QString url, QString path){
|
|
||||||
int index = subThreads.indexOf(st);
|
|
||||||
Q_ASSERT(index != -1);
|
|
||||||
subThreads.removeAt(index);
|
|
||||||
delete st;
|
|
||||||
emit downloadFinished(url, path);
|
|
||||||
mutex.lock();
|
|
||||||
index = downloading_list.indexOf(url);
|
|
||||||
Q_ASSERT(index != -1);
|
|
||||||
downloading_list.removeAt(index);
|
|
||||||
mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void propagateDownloadFailure(subDownloadThread* st, QString url, QString reason){
|
protected slots:
|
||||||
int index = subThreads.indexOf(st);
|
void propagateDownloadedFile(subDownloadThread* st, QString url, QString path);
|
||||||
Q_ASSERT(index != -1);
|
void propagateDownloadFailure(subDownloadThread* st, QString url, QString reason);
|
||||||
subThreads.removeAt(index);
|
|
||||||
delete st;
|
|
||||||
emit downloadFailure(url, reason);
|
|
||||||
mutex.lock();
|
|
||||||
index = downloading_list.indexOf(url);
|
|
||||||
Q_ASSERT(index != -1);
|
|
||||||
downloading_list.removeAt(index);
|
|
||||||
mutex.unlock();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
|
|||||||
actionTorrent_Properties->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/properties.png")));
|
actionTorrent_Properties->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/properties.png")));
|
||||||
// tabBottom->setTabIcon(0, QIcon(QString::fromUtf8(":/Icons/log.png")));
|
// tabBottom->setTabIcon(0, QIcon(QString::fromUtf8(":/Icons/log.png")));
|
||||||
// tabBottom->setTabIcon(1, QIcon(QString::fromUtf8(":/Icons/filter.png")));
|
// tabBottom->setTabIcon(1, QIcon(QString::fromUtf8(":/Icons/filter.png")));
|
||||||
// Set default ratio
|
|
||||||
lbl_ratio_icon->setPixmap(QPixmap(QString::fromUtf8(":/Icons/stare.png")));
|
|
||||||
|
|
||||||
// Set Download list model
|
// Set Download list model
|
||||||
DLListModel = new QStandardItemModel(0,9);
|
DLListModel = new QStandardItemModel(0,9);
|
||||||
@@ -65,6 +63,7 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
|
|||||||
downloadList->setItemDelegate(DLDelegate);
|
downloadList->setItemDelegate(DLDelegate);
|
||||||
// Hide hash column
|
// Hide hash column
|
||||||
downloadList->hideColumn(HASH);
|
downloadList->hideColumn(HASH);
|
||||||
|
loadHiddenColumns();
|
||||||
|
|
||||||
connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), this, SLOT(torrentAdded(QString, QTorrentHandle&, bool)));
|
connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), this, SLOT(torrentAdded(QString, QTorrentHandle&, bool)));
|
||||||
connect(BTSession, SIGNAL(duplicateTorrent(QString)), this, SLOT(torrentDuplicate(QString)));
|
connect(BTSession, SIGNAL(duplicateTorrent(QString)), this, SLOT(torrentDuplicate(QString)));
|
||||||
@@ -74,6 +73,8 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
|
|||||||
connect(BTSession, SIGNAL(fastResumeDataRejected(QString)), this, SLOT(addFastResumeRejectedAlert(QString)));
|
connect(BTSession, SIGNAL(fastResumeDataRejected(QString)), this, SLOT(addFastResumeRejectedAlert(QString)));
|
||||||
connect(BTSession, SIGNAL(aboutToDownloadFromUrl(QString)), this, SLOT(displayDownloadingUrlInfos(QString)));
|
connect(BTSession, SIGNAL(aboutToDownloadFromUrl(QString)), this, SLOT(displayDownloadingUrlInfos(QString)));
|
||||||
connect(BTSession, SIGNAL(urlSeedProblem(QString, QString)), this, SLOT(addUrlSeedError(QString, QString)));
|
connect(BTSession, SIGNAL(urlSeedProblem(QString, QString)), this, SLOT(addUrlSeedError(QString, QString)));
|
||||||
|
connect(BTSession, SIGNAL(UPnPError(QString)), this, SLOT(displayUPnPError(QString)));
|
||||||
|
connect(BTSession, SIGNAL(UPnPSuccess(QString)), this, SLOT(displayUPnPSuccess(QString)));
|
||||||
|
|
||||||
// Load last columns width for download list
|
// Load last columns width for download list
|
||||||
if(!loadColWidthDLList()) {
|
if(!loadColWidthDLList()) {
|
||||||
@@ -86,6 +87,8 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
|
|||||||
connect(downloadList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(notifyTorrentDoubleClicked(const QModelIndex&)));
|
connect(downloadList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(notifyTorrentDoubleClicked(const QModelIndex&)));
|
||||||
connect(downloadList->header(), SIGNAL(sectionPressed(int)), this, SLOT(sortDownloadList(int)));
|
connect(downloadList->header(), SIGNAL(sectionPressed(int)), this, SLOT(sortDownloadList(int)));
|
||||||
connect(downloadList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayDLListMenu(const QPoint&)));
|
connect(downloadList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayDLListMenu(const QPoint&)));
|
||||||
|
downloadList->header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(downloadList->header(), SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayDLHoSMenu(const QPoint&)));
|
||||||
connect(infoBar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayInfoBarMenu(const QPoint&)));
|
connect(infoBar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayInfoBarMenu(const QPoint&)));
|
||||||
// Actions
|
// Actions
|
||||||
connect(actionPause, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPause_triggered()));
|
connect(actionPause, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPause_triggered()));
|
||||||
@@ -93,15 +96,27 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
|
|||||||
connect(actionDelete, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_triggered()));
|
connect(actionDelete, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_triggered()));
|
||||||
connect(actionPreview_file, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPreview_file_triggered()));
|
connect(actionPreview_file, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPreview_file_triggered()));
|
||||||
connect(actionDelete_Permanently, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_Permanently_triggered()));
|
connect(actionDelete_Permanently, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_Permanently_triggered()));
|
||||||
|
connect(actionOpen_destination_folder, SIGNAL(triggered()), (GUI*)parent, SLOT(openDestinationFolder()));
|
||||||
connect(actionTorrent_Properties, SIGNAL(triggered()), this, SLOT(propertiesSelection()));
|
connect(actionTorrent_Properties, SIGNAL(triggered()), this, SLOT(propertiesSelection()));
|
||||||
|
connect(actionBuy_it, SIGNAL(triggered()), (GUI*)parent, SLOT(goBuyPage()));
|
||||||
|
|
||||||
|
connect(actionHOSColName, SIGNAL(triggered()), this, SLOT(hideOrShowColumnName()));
|
||||||
|
connect(actionHOSColSize, SIGNAL(triggered()), this, SLOT(hideOrShowColumnSize()));
|
||||||
|
connect(actionHOSColProgress, SIGNAL(triggered()), this, SLOT(hideOrShowColumnProgress()));
|
||||||
|
connect(actionHOSColDownSpeed, SIGNAL(triggered()), this, SLOT(hideOrShowColumnDownSpeed()));
|
||||||
|
connect(actionHOSColUpSpeed, SIGNAL(triggered()), this, SLOT(hideOrShowColumnUpSpeed()));
|
||||||
|
connect(actionHOSColSeedersLeechers, SIGNAL(triggered()), this, SLOT(hideOrShowColumnSeedersLeechers()));
|
||||||
|
connect(actionHOSColRatio, SIGNAL(triggered()), this, SLOT(hideOrShowColumnRatio()));
|
||||||
|
connect(actionHOSColEta, SIGNAL(triggered()), this, SLOT(hideOrShowColumnEta()));
|
||||||
|
|
||||||
// Set info Bar infos
|
// Set info Bar infos
|
||||||
setInfoBar(tr("qBittorrent %1 started.", "e.g: qBittorrent v0.x started.").arg(QString::fromUtf8(""VERSION)));
|
setInfoBar(tr("qBittorrent %1 started.", "e.g: qBittorrent v0.x started.").arg(QString::fromUtf8(""VERSION)));
|
||||||
setInfoBar(tr("Be careful, sharing copyrighted material without permission is against the law."), QString::fromUtf8("red"));
|
|
||||||
qDebug("Download tab built");
|
qDebug("Download tab built");
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadingTorrents::~DownloadingTorrents() {
|
DownloadingTorrents::~DownloadingTorrents() {
|
||||||
saveColWidthDLList();
|
saveColWidthDLList();
|
||||||
|
saveHiddenColumns();
|
||||||
delete DLDelegate;
|
delete DLDelegate;
|
||||||
delete DLListModel;
|
delete DLListModel;
|
||||||
}
|
}
|
||||||
@@ -110,7 +125,7 @@ DownloadingTorrents::~DownloadingTorrents() {
|
|||||||
void DownloadingTorrents::notifyTorrentDoubleClicked(const QModelIndex& index) {
|
void DownloadingTorrents::notifyTorrentDoubleClicked(const QModelIndex& index) {
|
||||||
unsigned int row = index.row();
|
unsigned int row = index.row();
|
||||||
QString hash = getHashFromRow(row);
|
QString hash = getHashFromRow(row);
|
||||||
emit torrentDoubleClicked(hash);
|
emit torrentDoubleClicked(hash, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadingTorrents::addLogPeerBlocked(QString ip) {
|
void DownloadingTorrents::addLogPeerBlocked(QString ip) {
|
||||||
@@ -130,7 +145,8 @@ unsigned int DownloadingTorrents::getNbTorrentsInList() const {
|
|||||||
// Note: do not actually pause the torrent in BT session
|
// Note: do not actually pause the torrent in BT session
|
||||||
void DownloadingTorrents::pauseTorrent(QString hash) {
|
void DownloadingTorrents::pauseTorrent(QString hash) {
|
||||||
int row = getRowFromHash(hash);
|
int row = getRowFromHash(hash);
|
||||||
Q_ASSERT(row != -1);
|
if(row == -1)
|
||||||
|
return;
|
||||||
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)0.0));
|
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)0.0));
|
||||||
DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)0.0));
|
DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)0.0));
|
||||||
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
|
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
|
||||||
@@ -154,11 +170,14 @@ void DownloadingTorrents::setBottomTabEnabled(unsigned int index, bool b){
|
|||||||
|
|
||||||
// Show torrent properties dialog
|
// Show torrent properties dialog
|
||||||
void DownloadingTorrents::showProperties(const QModelIndex &index) {
|
void DownloadingTorrents::showProperties(const QModelIndex &index) {
|
||||||
int row = index.row();
|
showPropertiesFromHash(DLListModel->data(DLListModel->index(index.row(), HASH)).toString());
|
||||||
QString hash = DLListModel->data(DLListModel->index(row, HASH)).toString();
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::showPropertiesFromHash(QString hash) {
|
||||||
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
properties *prop = new properties(this, BTSession, h);
|
properties *prop = new properties(this, BTSession, h);
|
||||||
connect(prop, SIGNAL(filteredFilesChanged(QString)), this, SLOT(updateFileSizeAndProgress(QString)));
|
connect(prop, SIGNAL(filteredFilesChanged(QString)), this, SLOT(updateFileSizeAndProgress(QString)));
|
||||||
|
connect(prop, SIGNAL(trackersChanged(QString)), BTSession, SLOT(saveTrackerFile(QString)));
|
||||||
prop->show();
|
prop->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,8 +200,16 @@ void DownloadingTorrents::deleteTorrent(QString hash) {
|
|||||||
emit unfinishedTorrentsNumberChanged(nbTorrents);
|
emit unfinishedTorrentsNumberChanged(nbTorrents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::displayUPnPError(QString msg) {
|
||||||
|
setInfoBar(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(msg), QColor("red"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::displayUPnPSuccess(QString msg) {
|
||||||
|
DownloadingTorrents::setInfoBar(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(msg), QColor("blue"));
|
||||||
|
}
|
||||||
|
|
||||||
// Update Info Bar information
|
// Update Info Bar information
|
||||||
void DownloadingTorrents::setInfoBar(QString info, QString color) {
|
void DownloadingTorrents::setInfoBar(QString info, QColor color) {
|
||||||
static unsigned int nbLines = 0;
|
static unsigned int nbLines = 0;
|
||||||
++nbLines;
|
++nbLines;
|
||||||
// Check log size, clear it if too big
|
// Check log size, clear it if too big
|
||||||
@@ -190,7 +217,7 @@ void DownloadingTorrents::setInfoBar(QString info, QString color) {
|
|||||||
infoBar->clear();
|
infoBar->clear();
|
||||||
nbLines = 1;
|
nbLines = 1;
|
||||||
}
|
}
|
||||||
infoBar->append(QString::fromUtf8("<font color='grey'>")+ QTime::currentTime().toString(QString::fromUtf8("hh:mm:ss")) + QString::fromUtf8("</font> - <font color='") + color +QString::fromUtf8("'><i>") + info + QString::fromUtf8("</i></font>"));
|
infoBar->append(QString::fromUtf8("<font color='grey'>")+ QTime::currentTime().toString(QString::fromUtf8("hh:mm:ss")) + QString::fromUtf8("</font> - <font color='") + color.name() +QString::fromUtf8("'><i>") + info + QString::fromUtf8("</i></font>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadingTorrents::addFastResumeRejectedAlert(QString name) {
|
void DownloadingTorrents::addFastResumeRejectedAlert(QString name) {
|
||||||
@@ -245,8 +272,6 @@ void DownloadingTorrents::displayDLListMenu(const QPoint& pos) {
|
|||||||
QModelIndex index;
|
QModelIndex index;
|
||||||
// Enable/disable pause/start action given the DL state
|
// Enable/disable pause/start action given the DL state
|
||||||
QModelIndexList selectedIndexes = downloadList->selectionModel()->selectedIndexes();
|
QModelIndexList selectedIndexes = downloadList->selectionModel()->selectedIndexes();
|
||||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
|
||||||
QString previewProgram = settings.value(QString::fromUtf8("Options/Misc/PreviewProgram"), QString()).toString();
|
|
||||||
bool has_pause = false, has_start = false, has_preview = false;
|
bool has_pause = false, has_start = false, has_preview = false;
|
||||||
foreach(index, selectedIndexes) {
|
foreach(index, selectedIndexes) {
|
||||||
if(index.column() == NAME) {
|
if(index.column() == NAME) {
|
||||||
@@ -266,7 +291,7 @@ void DownloadingTorrents::displayDLListMenu(const QPoint& pos) {
|
|||||||
has_pause = true;
|
has_pause = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!previewProgram.isEmpty() && BTSession->isFilePreviewPossible(hash) && !has_preview) {
|
if(BTSession->isFilePreviewPossible(hash) && !has_preview) {
|
||||||
myDLLlistMenu.addAction(actionPreview_file);
|
myDLLlistMenu.addAction(actionPreview_file);
|
||||||
has_preview = true;
|
has_preview = true;
|
||||||
}
|
}
|
||||||
@@ -280,16 +305,174 @@ void DownloadingTorrents::displayDLListMenu(const QPoint& pos) {
|
|||||||
myDLLlistMenu.addAction(actionSet_download_limit);
|
myDLLlistMenu.addAction(actionSet_download_limit);
|
||||||
myDLLlistMenu.addAction(actionSet_upload_limit);
|
myDLLlistMenu.addAction(actionSet_upload_limit);
|
||||||
myDLLlistMenu.addSeparator();
|
myDLLlistMenu.addSeparator();
|
||||||
|
myDLLlistMenu.addAction(actionOpen_destination_folder);
|
||||||
myDLLlistMenu.addAction(actionTorrent_Properties);
|
myDLLlistMenu.addAction(actionTorrent_Properties);
|
||||||
|
myDLLlistMenu.addSeparator();
|
||||||
|
myDLLlistMenu.addAction(actionBuy_it);
|
||||||
// Call menu
|
// Call menu
|
||||||
// XXX: why mapToGlobal() is not enough?
|
// XXX: why mapToGlobal() is not enough?
|
||||||
myDLLlistMenu.exec(mapToGlobal(pos)+QPoint(10,60));
|
myDLLlistMenu.exec(mapToGlobal(pos)+QPoint(10,60));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hiding Columns functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// hide/show columns menu
|
||||||
|
void DownloadingTorrents::displayDLHoSMenu(const QPoint& pos){
|
||||||
|
QMenu hideshowColumn(this);
|
||||||
|
hideshowColumn.setTitle(tr("Hide or Show Column"));
|
||||||
|
for(int i=0; i<=ETA; i++) {
|
||||||
|
hideshowColumn.addAction(getActionHoSCol(i));
|
||||||
|
}
|
||||||
|
// Call menu
|
||||||
|
hideshowColumn.exec(mapToGlobal(pos)+QPoint(10,55));
|
||||||
|
}
|
||||||
|
|
||||||
|
// toggle hide/show a column
|
||||||
|
void DownloadingTorrents::hideOrShowColumn(int index) {
|
||||||
|
unsigned int nbVisibleColumns = 0;
|
||||||
|
unsigned int nbCols = DLListModel->columnCount();
|
||||||
|
// Count visible columns
|
||||||
|
for(unsigned int i=0; i<nbCols; ++i) {
|
||||||
|
if(!downloadList->isColumnHidden(i))
|
||||||
|
++nbVisibleColumns;
|
||||||
|
}
|
||||||
|
if(!downloadList->isColumnHidden(index)) {
|
||||||
|
// User wants to hide the column
|
||||||
|
// Is there at least one other visible column?
|
||||||
|
if(nbVisibleColumns <= 1) return;
|
||||||
|
// User can hide the column, do it.
|
||||||
|
downloadList->setColumnHidden(index, true);
|
||||||
|
getActionHoSCol(index)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
|
||||||
|
--nbVisibleColumns;
|
||||||
|
} else {
|
||||||
|
// User want to display the column
|
||||||
|
downloadList->setColumnHidden(index, false);
|
||||||
|
getActionHoSCol(index)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
|
||||||
|
++nbVisibleColumns;
|
||||||
|
}
|
||||||
|
//resize all others non-hidden columns
|
||||||
|
for(unsigned int i=0; i<nbCols; ++i) {
|
||||||
|
if(!downloadList->isColumnHidden(i)) {
|
||||||
|
downloadList->setColumnWidth(i, (int)ceil(downloadList->columnWidth(i)+(downloadList->columnWidth(index)/nbVisibleColumns)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the hidden columns in settings
|
||||||
|
void DownloadingTorrents::saveHiddenColumns() {
|
||||||
|
QSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
QStringList ishidden_list;
|
||||||
|
short nbColumns = DLListModel->columnCount()-1;
|
||||||
|
|
||||||
|
for(short i=0; i<nbColumns; ++i){
|
||||||
|
if(downloadList->isColumnHidden(i)) {
|
||||||
|
ishidden_list << QString::fromUtf8(misc::toString(0).c_str());
|
||||||
|
} else {
|
||||||
|
ishidden_list << QString::fromUtf8(misc::toString(1).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.setValue("DownloadListColsHoS", ishidden_list.join(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the previous settings, and hide the columns
|
||||||
|
bool DownloadingTorrents::loadHiddenColumns() {
|
||||||
|
bool loaded = false;
|
||||||
|
QSettings settings("qBittorrent", "qBittorrent");
|
||||||
|
QString line = settings.value("DownloadListColsHoS", QString()).toString();
|
||||||
|
QStringList ishidden_list;
|
||||||
|
if(!line.isEmpty()) {
|
||||||
|
ishidden_list = line.split(' ');
|
||||||
|
if(ishidden_list.size() == DLListModel->columnCount()-1) {
|
||||||
|
unsigned int listSize = ishidden_list.size();
|
||||||
|
for(unsigned int i=0; i<listSize; ++i){
|
||||||
|
downloadList->header()->resizeSection(i, ishidden_list.at(i).toInt());
|
||||||
|
}
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(int i=0; i<DLListModel->columnCount()-1; i++) {
|
||||||
|
if(loaded && ishidden_list.at(i) == "0") {
|
||||||
|
downloadList->setColumnHidden(i, true);
|
||||||
|
getActionHoSCol(i)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
|
||||||
|
} else {
|
||||||
|
getActionHoSCol(i)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::hideOrShowColumnName() {
|
||||||
|
hideOrShowColumn(NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::hideOrShowColumnSize() {
|
||||||
|
hideOrShowColumn(SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::hideOrShowColumnProgress() {
|
||||||
|
hideOrShowColumn(PROGRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::hideOrShowColumnDownSpeed() {
|
||||||
|
hideOrShowColumn(DLSPEED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::hideOrShowColumnUpSpeed() {
|
||||||
|
hideOrShowColumn(UPSPEED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::hideOrShowColumnSeedersLeechers() {
|
||||||
|
hideOrShowColumn(SEEDSLEECH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::hideOrShowColumnRatio() {
|
||||||
|
hideOrShowColumn(RATIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DownloadingTorrents::hideOrShowColumnEta() {
|
||||||
|
hideOrShowColumn(ETA);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void DownloadingTorrents::on_actionClearLog_triggered() {
|
void DownloadingTorrents::on_actionClearLog_triggered() {
|
||||||
infoBar->clear();
|
infoBar->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getter, return the action hide or show whose id is index
|
||||||
|
QAction* DownloadingTorrents::getActionHoSCol(int index) {
|
||||||
|
switch(index) {
|
||||||
|
case NAME :
|
||||||
|
return actionHOSColName;
|
||||||
|
break;
|
||||||
|
case SIZE :
|
||||||
|
return actionHOSColSize;
|
||||||
|
break;
|
||||||
|
case PROGRESS :
|
||||||
|
return actionHOSColProgress;
|
||||||
|
break;
|
||||||
|
case DLSPEED :
|
||||||
|
return actionHOSColDownSpeed;
|
||||||
|
break;
|
||||||
|
case UPSPEED :
|
||||||
|
return actionHOSColUpSpeed;
|
||||||
|
break;
|
||||||
|
case SEEDSLEECH :
|
||||||
|
return actionHOSColSeedersLeechers;
|
||||||
|
break;
|
||||||
|
case RATIO :
|
||||||
|
return actionHOSColRatio;
|
||||||
|
break;
|
||||||
|
case ETA :
|
||||||
|
return actionHOSColEta;
|
||||||
|
break;
|
||||||
|
default :
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QStringList DownloadingTorrents::getSelectedTorrents(bool only_one) const{
|
QStringList DownloadingTorrents::getSelectedTorrents(bool only_one) const{
|
||||||
QStringList res;
|
QStringList res;
|
||||||
QModelIndex index;
|
QModelIndex index;
|
||||||
@@ -305,38 +488,12 @@ QStringList DownloadingTorrents::getSelectedTorrents(bool only_one) const{
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadingTorrents::updateRatio() {
|
|
||||||
// Update ratio info
|
|
||||||
float ratio = 1.;
|
|
||||||
session_status sessionStatus = BTSession->getSessionStatus();
|
|
||||||
if(sessionStatus.total_payload_download == 0) {
|
|
||||||
if(sessionStatus.total_payload_upload == 0)
|
|
||||||
ratio = 1.;
|
|
||||||
else
|
|
||||||
ratio = 10.;
|
|
||||||
}else{
|
|
||||||
ratio = (double)sessionStatus.total_payload_upload / (double)sessionStatus.total_payload_download;
|
|
||||||
if(ratio > 10.)
|
|
||||||
ratio = 10.;
|
|
||||||
}
|
|
||||||
LCD_Ratio->display(QString(QByteArray::number(ratio, 'f', 1)));
|
|
||||||
if(ratio < 0.5) {
|
|
||||||
lbl_ratio_icon->setPixmap(QPixmap(QString::fromUtf8(":/Icons/unhappy.png")));
|
|
||||||
}else{
|
|
||||||
if(ratio > 1.0) {
|
|
||||||
lbl_ratio_icon->setPixmap(QPixmap(QString::fromUtf8(":/Icons/smile.png")));
|
|
||||||
}else{
|
|
||||||
lbl_ratio_icon->setPixmap(QPixmap(QString::fromUtf8(":/Icons/stare.png")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DownloadingTorrents::displayInfoBarMenu(const QPoint& pos) {
|
void DownloadingTorrents::displayInfoBarMenu(const QPoint& pos) {
|
||||||
// Log Menu
|
// Log Menu
|
||||||
QMenu myLogMenu(this);
|
QMenu myLogMenu(this);
|
||||||
myLogMenu.addAction(actionClearLog);
|
myLogMenu.addAction(actionClearLog);
|
||||||
// XXX: Why mapToGlobal() is not enough?
|
// XXX: Why mapToGlobal() is not enough?
|
||||||
myLogMenu.exec(mapToGlobal(pos)+QPoint(22,383));
|
myLogMenu.exec(mapToGlobal(pos)+QPoint(44,305));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadingTorrents::sortProgressColumnDelayed() {
|
void DownloadingTorrents::sortProgressColumnDelayed() {
|
||||||
@@ -349,9 +506,6 @@ void DownloadingTorrents::sortProgressColumnDelayed() {
|
|||||||
// get information from torrent handles and
|
// get information from torrent handles and
|
||||||
// update download list accordingly
|
// update download list accordingly
|
||||||
void DownloadingTorrents::updateDlList() {
|
void DownloadingTorrents::updateDlList() {
|
||||||
// update global informations
|
|
||||||
LCD_UpSpeed->display(QString(QByteArray::number(BTSession->getPayloadUploadRate()/1024., 'f', 1))); // UP LCD
|
|
||||||
LCD_DownSpeed->display(QString(QByteArray::number(BTSession->getPayloadDownloadRate()/1024., 'f', 1))); // DL LCD
|
|
||||||
// browse handles
|
// browse handles
|
||||||
QStringList unfinishedTorrents = BTSession->getUnfinishedTorrents();
|
QStringList unfinishedTorrents = BTSession->getUnfinishedTorrents();
|
||||||
QString hash;
|
QString hash;
|
||||||
@@ -372,6 +526,12 @@ void DownloadingTorrents::updateDlList() {
|
|||||||
Q_ASSERT(row != -1);
|
Q_ASSERT(row != -1);
|
||||||
// No need to update a paused torrent
|
// No need to update a paused torrent
|
||||||
if(h.is_paused()) continue;
|
if(h.is_paused()) continue;
|
||||||
|
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) != -1) {
|
||||||
|
if(!downloadList->isColumnHidden(PROGRESS)) {
|
||||||
|
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Parse download state
|
// Parse download state
|
||||||
// Setting download state
|
// Setting download state
|
||||||
switch(h.state()) {
|
switch(h.state()) {
|
||||||
@@ -384,47 +544,73 @@ void DownloadingTorrents::updateDlList() {
|
|||||||
continue;
|
continue;
|
||||||
case torrent_status::checking_files:
|
case torrent_status::checking_files:
|
||||||
case torrent_status::queued_for_checking:
|
case torrent_status::queued_for_checking:
|
||||||
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) == -1) {
|
|
||||||
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
|
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
|
||||||
setRowColor(row, QString::fromUtf8("grey"));
|
setRowColor(row, QString::fromUtf8("grey"));
|
||||||
}
|
if(!downloadList->isColumnHidden(PROGRESS)) {
|
||||||
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case torrent_status::connecting_to_tracker:
|
case torrent_status::connecting_to_tracker:
|
||||||
if(h.download_payload_rate() > 0) {
|
if(h.download_payload_rate() > 0) {
|
||||||
// Display "Downloading" status when connecting if download speed > 0
|
// Display "Downloading" status when connecting if download speed > 0
|
||||||
|
if(!downloadList->isColumnHidden(ETA)) {
|
||||||
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)BTSession->getETA(hash)));
|
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)BTSession->getETA(hash)));
|
||||||
|
}
|
||||||
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png"))), Qt::DecorationRole);
|
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png"))), Qt::DecorationRole);
|
||||||
setRowColor(row, QString::fromUtf8("green"));
|
setRowColor(row, QString::fromUtf8("green"));
|
||||||
}else{
|
}else{
|
||||||
|
if(!downloadList->isColumnHidden(ETA)) {
|
||||||
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
|
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
|
||||||
|
}
|
||||||
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/connecting.png"))), Qt::DecorationRole);
|
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/connecting.png"))), Qt::DecorationRole);
|
||||||
setRowColor(row, QString::fromUtf8("grey"));
|
setRowColor(row, QString::fromUtf8("grey"));
|
||||||
}
|
}
|
||||||
|
if(!downloadList->isColumnHidden(PROGRESS)) {
|
||||||
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
||||||
|
}
|
||||||
|
if(!downloadList->isColumnHidden(DLSPEED)) {
|
||||||
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate()));
|
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate()));
|
||||||
|
}
|
||||||
|
if(!downloadList->isColumnHidden(UPSPEED)) {
|
||||||
DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)h.upload_payload_rate()));
|
DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)h.upload_payload_rate()));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case torrent_status::downloading:
|
case torrent_status::downloading:
|
||||||
case torrent_status::downloading_metadata:
|
case torrent_status::downloading_metadata:
|
||||||
if(h.download_payload_rate() > 0) {
|
if(h.download_payload_rate() > 0) {
|
||||||
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png"))), Qt::DecorationRole);
|
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png"))), Qt::DecorationRole);
|
||||||
|
if(!downloadList->isColumnHidden(ETA)) {
|
||||||
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)BTSession->getETA(hash)));
|
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)BTSession->getETA(hash)));
|
||||||
|
}
|
||||||
setRowColor(row, QString::fromUtf8("green"));
|
setRowColor(row, QString::fromUtf8("green"));
|
||||||
}else{
|
}else{
|
||||||
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/stalled.png"))), Qt::DecorationRole);
|
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/stalled.png"))), Qt::DecorationRole);
|
||||||
|
if(!downloadList->isColumnHidden(ETA)) {
|
||||||
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
|
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
|
||||||
setRowColor(row, QString::fromUtf8("black"));
|
|
||||||
}
|
}
|
||||||
|
setRowColor(row, QApplication::palette().color(QPalette::WindowText));
|
||||||
|
}
|
||||||
|
if(!downloadList->isColumnHidden(PROGRESS)) {
|
||||||
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
|
||||||
|
}
|
||||||
|
if(!downloadList->isColumnHidden(DLSPEED)) {
|
||||||
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate()));
|
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate()));
|
||||||
|
}
|
||||||
|
if(!downloadList->isColumnHidden(UPSPEED)) {
|
||||||
DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)h.upload_payload_rate()));
|
DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)h.upload_payload_rate()));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if(!downloadList->isColumnHidden(ETA)) {
|
||||||
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
|
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if(!downloadList->isColumnHidden(SEEDSLEECH)) {
|
||||||
DLListModel->setData(DLListModel->index(row, SEEDSLEECH), QVariant(misc::toQString(h.num_seeds(), true)+QString::fromUtf8("/")+misc::toQString(h.num_peers() - h.num_seeds(), true)));
|
DLListModel->setData(DLListModel->index(row, SEEDSLEECH), QVariant(misc::toQString(h.num_seeds(), true)+QString::fromUtf8("/")+misc::toQString(h.num_peers() - h.num_seeds(), true)));
|
||||||
|
}
|
||||||
|
if(!downloadList->isColumnHidden(RATIO)) {
|
||||||
DLListModel->setData(DLListModel->index(row, RATIO), QVariant(misc::toQString(BTSession->getRealRatio(hash))));
|
DLListModel->setData(DLListModel->index(row, RATIO), QVariant(misc::toQString(BTSession->getRealRatio(hash))));
|
||||||
|
}
|
||||||
}catch(invalid_handle e) {
|
}catch(invalid_handle e) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -560,11 +746,26 @@ void DownloadingTorrents::saveColWidthDLList() const{
|
|||||||
qDebug("Saving columns width in download list");
|
qDebug("Saving columns width in download list");
|
||||||
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||||
QStringList width_list;
|
QStringList width_list;
|
||||||
unsigned int nbColumns = DLListModel->columnCount()-1;
|
QStringList new_width_list;
|
||||||
for(unsigned int i=0; i<nbColumns; ++i) {
|
short nbColumns = DLListModel->columnCount()-1;
|
||||||
width_list << misc::toQString(downloadList->columnWidth(i));
|
QString line = settings.value("DownloadListColsWidth", QString()).toString();
|
||||||
|
if(!line.isEmpty()) {
|
||||||
|
width_list = line.split(' ');
|
||||||
}
|
}
|
||||||
settings.setValue(QString::fromUtf8("DownloadListColsWidth"), width_list.join(QString::fromUtf8(" ")));
|
for(short i=0; i<nbColumns; ++i){
|
||||||
|
if(downloadList->columnWidth(i)<1 && width_list.size() == DLListModel->columnCount()-1 && width_list.at(i).toInt()>=1) {
|
||||||
|
// load the former width
|
||||||
|
new_width_list << width_list.at(i);
|
||||||
|
} else if(downloadList->columnWidth(i)>=1) {
|
||||||
|
// usual case, save the current width
|
||||||
|
new_width_list << QString::fromUtf8(misc::toString(downloadList->columnWidth(i)).c_str());
|
||||||
|
} else {
|
||||||
|
// default width
|
||||||
|
downloadList->resizeColumnToContents(i);
|
||||||
|
new_width_list << QString::fromUtf8(misc::toString(downloadList->columnWidth(i)).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.setValue(QString::fromUtf8("DownloadListColsWidth"), new_width_list.join(QString::fromUtf8(" ")));
|
||||||
qDebug("Download list columns width saved");
|
qDebug("Download list columns width saved");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,10 +862,10 @@ void DownloadingTorrents::portListeningFailure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the color of a row in data model
|
// Set the color of a row in data model
|
||||||
void DownloadingTorrents::setRowColor(int row, QString color) {
|
void DownloadingTorrents::setRowColor(int row, QColor color) {
|
||||||
unsigned int nbColumns = DLListModel->columnCount();
|
unsigned int nbColumns = DLListModel->columnCount()-1;
|
||||||
for(unsigned int i=0; i<nbColumns; ++i) {
|
for(unsigned int i=0; i<nbColumns; ++i) {
|
||||||
DLListModel->setData(DLListModel->index(row, i), QVariant(QColor(color)), Qt::ForegroundRole);
|
DLListModel->setData(DLListModel->index(row, i), QVariant(color), Qt::ForegroundRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,5 +882,5 @@ int DownloadingTorrents::getRowFromHash(QString hash) const{
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DownloadingTorrents::displayDownloadingUrlInfos(QString url) {
|
void DownloadingTorrents::displayDownloadingUrlInfos(QString url) {
|
||||||
setInfoBar(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(url), QString::fromUtf8("black"));
|
setInfoBar(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(url), QPalette::WindowText);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ class DownloadingTorrents : public QWidget, public Ui::downloading{
|
|||||||
bool delayedSorting;
|
bool delayedSorting;
|
||||||
unsigned int nbTorrents;
|
unsigned int nbTorrents;
|
||||||
Qt::SortOrder delayedSortingOrder;
|
Qt::SortOrder delayedSortingOrder;
|
||||||
|
void hideOrShowColumn(int index);
|
||||||
|
bool loadHiddenColumns();
|
||||||
|
void saveHiddenColumns();
|
||||||
|
QAction* getActionHoSCol(int index);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DownloadingTorrents(QObject *parent, bittorrent *BTSession);
|
DownloadingTorrents(QObject *parent, bittorrent *BTSession);
|
||||||
@@ -54,7 +58,7 @@ class DownloadingTorrents : public QWidget, public Ui::downloading{
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void unfinishedTorrentsNumberChanged(unsigned int);
|
void unfinishedTorrentsNumberChanged(unsigned int);
|
||||||
void torrentDoubleClicked(QString hash);
|
void torrentDoubleClicked(QString hash, bool finished);
|
||||||
void torrentFinished(QString hash);
|
void torrentFinished(QString hash);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
@@ -65,6 +69,7 @@ class DownloadingTorrents : public QWidget, public Ui::downloading{
|
|||||||
void notifyTorrentDoubleClicked(const QModelIndex& index);
|
void notifyTorrentDoubleClicked(const QModelIndex& index);
|
||||||
void on_actionSet_upload_limit_triggered();
|
void on_actionSet_upload_limit_triggered();
|
||||||
void displayDLListMenu(const QPoint& pos);
|
void displayDLListMenu(const QPoint& pos);
|
||||||
|
void displayDLHoSMenu(const QPoint&);
|
||||||
void on_actionClearLog_triggered();
|
void on_actionClearLog_triggered();
|
||||||
void displayInfoBarMenu(const QPoint& pos);
|
void displayInfoBarMenu(const QPoint& pos);
|
||||||
void addTorrent(QString hash);
|
void addTorrent(QString hash);
|
||||||
@@ -76,21 +81,31 @@ class DownloadingTorrents : public QWidget, public Ui::downloading{
|
|||||||
void torrentDuplicate(QString path);
|
void torrentDuplicate(QString path);
|
||||||
void torrentCorrupted(QString path);
|
void torrentCorrupted(QString path);
|
||||||
void portListeningFailure();
|
void portListeningFailure();
|
||||||
void setRowColor(int row, QString color);
|
void setRowColor(int row, QColor color);
|
||||||
void displayDownloadingUrlInfos(QString url);
|
void displayDownloadingUrlInfos(QString url);
|
||||||
void showProperties(const QModelIndex &index);
|
void showProperties(const QModelIndex &index);
|
||||||
|
void hideOrShowColumnName();
|
||||||
|
void hideOrShowColumnSize();
|
||||||
|
void hideOrShowColumnProgress();
|
||||||
|
void hideOrShowColumnDownSpeed();
|
||||||
|
void hideOrShowColumnUpSpeed();
|
||||||
|
void hideOrShowColumnSeedersLeechers();
|
||||||
|
void hideOrShowColumnRatio();
|
||||||
|
void hideOrShowColumnEta();
|
||||||
|
void displayUPnPError(QString msg);
|
||||||
|
void displayUPnPSuccess(QString msg);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateDlList();
|
void updateDlList();
|
||||||
void setInfoBar(QString info, QString color="black");
|
void setInfoBar(QString info, QColor color=QApplication::palette().color(QPalette::WindowText));
|
||||||
void pauseTorrent(QString hash);
|
void pauseTorrent(QString hash);
|
||||||
void resumeTorrent(QString hash);
|
void resumeTorrent(QString hash);
|
||||||
void updateRatio();
|
|
||||||
void deleteTorrent(QString hash);
|
void deleteTorrent(QString hash);
|
||||||
void setBottomTabEnabled(unsigned int index, bool b);
|
void setBottomTabEnabled(unsigned int index, bool b);
|
||||||
void propertiesSelection();
|
void propertiesSelection();
|
||||||
void sortProgressColumnDelayed();
|
void sortProgressColumnDelayed();
|
||||||
void updateFileSizeAndProgress(QString hash);
|
void updateFileSizeAndProgress(QString hash);
|
||||||
|
void showPropertiesFromHash(QString hash);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
125
src/engineSelect.ui
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<ui version="4.0" >
|
||||||
|
<class>engineSelect</class>
|
||||||
|
<widget class="QDialog" name="engineSelect" >
|
||||||
|
<property name="geometry" >
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>541</width>
|
||||||
|
<height>254</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="acceptDrops" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle" >
|
||||||
|
<string>Search plugins</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_engines" >
|
||||||
|
<property name="font" >
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
<underline>true</underline>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Installed search engines:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeWidget" name="pluginsTree" >
|
||||||
|
<property name="contextMenuPolicy" >
|
||||||
|
<enum>Qt::CustomContextMenu</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode" >
|
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="uniformRowHeights" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="itemsExpandable" >
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<column>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Name</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Url</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Enabled</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text" >
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="getNewEngine_lbl" >
|
||||||
|
<property name="font" >
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>You can get new search engine plugins here: <a href="http:plugins.qbittorrent.org">http://plugins.qbittorrent.org</a></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" >
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="installButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Install a new one</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="updateButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Check for updates</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="closeButton" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Close</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
<action name="actionEnable" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Enable</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionDisable" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Disable</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="actionUninstall" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Uninstall</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
703
src/engineSelectDlg.cpp
Normal file
@@ -0,0 +1,703 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "engineSelectDlg.h"
|
||||||
|
#include "downloadThread.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "pluginSource.h"
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QDropEvent>
|
||||||
|
#include <QInputDialog>
|
||||||
|
|
||||||
|
#ifdef HAVE_MAGICK
|
||||||
|
#include <Magick++.h>
|
||||||
|
using namespace Magick;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_ZZIP
|
||||||
|
#include <zzip/zzip.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ENGINE_NAME 0
|
||||||
|
#define ENGINE_URL 1
|
||||||
|
#define ENGINE_STATE 2
|
||||||
|
#define ENGINE_ID 3
|
||||||
|
|
||||||
|
engineSelectDlg::engineSelectDlg(QWidget *parent) : QDialog(parent) {
|
||||||
|
setupUi(this);
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
pluginsTree->header()->resizeSection(0, 170);
|
||||||
|
pluginsTree->header()->resizeSection(1, 220);
|
||||||
|
pluginsTree->hideColumn(ENGINE_ID);
|
||||||
|
actionEnable->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
|
||||||
|
actionDisable->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
|
||||||
|
actionUninstall->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
|
||||||
|
connect(actionEnable, SIGNAL(triggered()), this, SLOT(enableSelection()));
|
||||||
|
connect(actionDisable, SIGNAL(triggered()), this, SLOT(disableSelection()));
|
||||||
|
connect(pluginsTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayContextMenu(const QPoint&)));
|
||||||
|
downloader = new downloadThread(this);
|
||||||
|
connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString)));
|
||||||
|
connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
|
||||||
|
loadSupportedSearchEngines(true);
|
||||||
|
connect(pluginsTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(toggleEngineState(QTreeWidgetItem*, int)));
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
engineSelectDlg::~engineSelectDlg() {
|
||||||
|
qDebug("Destroying engineSelectDlg");
|
||||||
|
saveSettings();
|
||||||
|
emit enginesChanged();
|
||||||
|
qDebug("Before deleting downloader");
|
||||||
|
delete downloader;
|
||||||
|
qDebug("Engine plugins dialog destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::dropEvent(QDropEvent *event) {
|
||||||
|
event->acceptProposedAction();
|
||||||
|
QStringList files=event->mimeData()->text().split(QString::fromUtf8("\n"));
|
||||||
|
QString file;
|
||||||
|
foreach(file, files) {
|
||||||
|
qDebug("dropped %s", file.toUtf8().data());
|
||||||
|
file = file.replace("file://", "");
|
||||||
|
if(file.startsWith("http://", Qt::CaseInsensitive) || file.startsWith("https://", Qt::CaseInsensitive) || file.startsWith("ftp://", Qt::CaseInsensitive)) {
|
||||||
|
downloader->downloadUrl(file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(file.endsWith(".py", Qt::CaseInsensitive)) {
|
||||||
|
QString plugin_name = file.split(QDir::separator()).last();
|
||||||
|
plugin_name.replace(".py", "");
|
||||||
|
installPlugin(file, plugin_name);
|
||||||
|
}
|
||||||
|
#ifdef HAVE_ZZIP
|
||||||
|
if(file.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||||
|
installZipPlugin(file);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode if we accept drag 'n drop or not
|
||||||
|
void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) {
|
||||||
|
QString mime;
|
||||||
|
foreach(mime, event->mimeData()->formats()){
|
||||||
|
qDebug("mimeData: %s", mime.toUtf8().data());
|
||||||
|
}
|
||||||
|
if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain")) || event->mimeData()->hasFormat(QString::fromUtf8("text/uri-list"))) {
|
||||||
|
event->acceptProposedAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::saveSettings() {
|
||||||
|
qDebug("Saving engines settings");
|
||||||
|
QStringList known_engines;
|
||||||
|
QVariantList known_enginesEnabled;
|
||||||
|
QString engine;
|
||||||
|
foreach(engine, installed_engines.keys()) {
|
||||||
|
known_engines << engine;
|
||||||
|
known_enginesEnabled << QVariant(installed_engines.value(engine, true));
|
||||||
|
qDebug("Engine %s has state: %d", engine.toUtf8().data(), installed_engines.value(engine, true));
|
||||||
|
}
|
||||||
|
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||||
|
settings.setValue(QString::fromUtf8("SearchEngines/knownEngines"), known_engines);
|
||||||
|
settings.setValue(QString::fromUtf8("SearchEngines/knownEnginesEnabled"), known_enginesEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::on_updateButton_clicked() {
|
||||||
|
// Download version file from primary server
|
||||||
|
downloader->downloadUrl("http://www.dchris.eu/search_engine/versions.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::toggleEngineState(QTreeWidgetItem *item, int) {
|
||||||
|
int index = pluginsTree->indexOfTopLevelItem(item);
|
||||||
|
QString id = item->text(ENGINE_ID);
|
||||||
|
bool new_val = !installed_engines.value(id, true);
|
||||||
|
installed_engines[id] = new_val;
|
||||||
|
QString enabledTxt;
|
||||||
|
if(new_val){
|
||||||
|
enabledTxt = tr("True");
|
||||||
|
setRowColor(index, "green");
|
||||||
|
}else{
|
||||||
|
enabledTxt = tr("False");
|
||||||
|
setRowColor(index, "red");
|
||||||
|
}
|
||||||
|
item->setText(ENGINE_STATE, enabledTxt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::displayContextMenu(const QPoint& pos) {
|
||||||
|
QMenu myContextMenu(this);
|
||||||
|
QModelIndex index;
|
||||||
|
// Enable/disable pause/start action given the DL state
|
||||||
|
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
||||||
|
bool has_enable = false, has_disable = false;
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
foreach(item, items) {
|
||||||
|
QString id = item->text(ENGINE_ID);
|
||||||
|
if(installed_engines.value(id, true) and !has_disable) {
|
||||||
|
myContextMenu.addAction(actionDisable);
|
||||||
|
has_disable = true;
|
||||||
|
}
|
||||||
|
if(!installed_engines.value(id, true) and !has_enable) {
|
||||||
|
myContextMenu.addAction(actionEnable);
|
||||||
|
has_enable = true;
|
||||||
|
}
|
||||||
|
if(has_enable && has_disable) break;
|
||||||
|
}
|
||||||
|
myContextMenu.addSeparator();
|
||||||
|
myContextMenu.addAction(actionUninstall);
|
||||||
|
myContextMenu.exec(mapToGlobal(pos)+QPoint(12, 58));
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::on_closeButton_clicked() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::on_actionUninstall_triggered() {
|
||||||
|
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
bool change = false;
|
||||||
|
bool error = false;
|
||||||
|
foreach(item, items) {
|
||||||
|
int index = pluginsTree->indexOfTopLevelItem(item);
|
||||||
|
Q_ASSERT(index != -1);
|
||||||
|
QString id = item->text(ENGINE_ID);
|
||||||
|
if(QFile::exists(":/search_engine/engines/"+id+".py")) {
|
||||||
|
error = true;
|
||||||
|
// Disable it instead
|
||||||
|
installed_engines.insert(id, false);
|
||||||
|
item->setText(ENGINE_STATE, tr("False"));
|
||||||
|
setRowColor(index, "red");
|
||||||
|
continue;
|
||||||
|
}else {
|
||||||
|
// Proceed with uninstall
|
||||||
|
// remove it from hard drive
|
||||||
|
QDir enginesFolder(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines");
|
||||||
|
QStringList filters;
|
||||||
|
filters << id+".*";
|
||||||
|
QStringList files = enginesFolder.entryList(filters, QDir::Files, QDir::Unsorted);
|
||||||
|
QString file;
|
||||||
|
foreach(file, files) {
|
||||||
|
enginesFolder.remove(file);
|
||||||
|
}
|
||||||
|
// Remove it from lists
|
||||||
|
installed_engines.remove(id);
|
||||||
|
delete item;
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(error)
|
||||||
|
QMessageBox::warning(0, tr("Uninstall warning"), tr("Some plugins could not be uninstalled because they are included in qBittorrent.\n Only the ones you added yourself can be uninstalled.\nHowever, those plugins were disabled."));
|
||||||
|
else
|
||||||
|
QMessageBox::information(0, tr("Uninstall success"), tr("All selected plugins were uninstalled successfully"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::enableSelection() {
|
||||||
|
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
foreach(item, items) {
|
||||||
|
int index = pluginsTree->indexOfTopLevelItem(item);
|
||||||
|
Q_ASSERT(index != -1);
|
||||||
|
QString id = item->text(ENGINE_ID);
|
||||||
|
installed_engines.insert(id, true);
|
||||||
|
item->setText(ENGINE_STATE, tr("True"));
|
||||||
|
setRowColor(index, "green");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::disableSelection() {
|
||||||
|
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
foreach(item, items) {
|
||||||
|
int index = pluginsTree->indexOfTopLevelItem(item);
|
||||||
|
Q_ASSERT(index != -1);
|
||||||
|
QString id = item->text(ENGINE_ID);
|
||||||
|
installed_engines.insert(id, false);
|
||||||
|
item->setText(ENGINE_STATE, tr("False"));
|
||||||
|
setRowColor(index, "red");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the color of a row in data model
|
||||||
|
void engineSelectDlg::setRowColor(int row, QString color){
|
||||||
|
QTreeWidgetItem *item = pluginsTree->topLevelItem(row);
|
||||||
|
for(int i=0; i<pluginsTree->columnCount()-1; ++i){
|
||||||
|
item->setData(i, Qt::ForegroundRole, QVariant(QColor(color)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool engineSelectDlg::checkInstalled(QString plugin_name) const {
|
||||||
|
QProcess nova;
|
||||||
|
QStringList params;
|
||||||
|
params << misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py";
|
||||||
|
params << "--supported_engines";
|
||||||
|
nova.start("python", params, QIODevice::ReadOnly);
|
||||||
|
nova.waitForStarted();
|
||||||
|
nova.waitForFinished();
|
||||||
|
QByteArray result = nova.readAll();
|
||||||
|
result = result.replace("\r", "");
|
||||||
|
result = result.replace("\n", "");
|
||||||
|
QList<QByteArray> plugins_list = result.split(',');
|
||||||
|
return plugins_list.contains(plugin_name.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::loadSupportedSearchEngines(bool first) {
|
||||||
|
// Some clean up first
|
||||||
|
pluginsTree->clear();
|
||||||
|
QHash<QString, bool> old_engines;
|
||||||
|
if(first) {
|
||||||
|
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
|
||||||
|
QStringList known_engines = settings.value(QString::fromUtf8("SearchEngines/knownEngines"), QStringList()).toStringList();
|
||||||
|
QVariantList enabled = settings.value(QString::fromUtf8("SearchEngines/knownEnginesEnabled"), QList<QVariant>()).toList();
|
||||||
|
Q_ASSERT(known_engines.size() == enabled.size());
|
||||||
|
unsigned int nbKnownEngines = known_engines.size();
|
||||||
|
for(unsigned int i=0; i<nbKnownEngines; ++i) {
|
||||||
|
old_engines[known_engines.at(i)] = enabled.at(i).toBool();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
old_engines = installed_engines;
|
||||||
|
}
|
||||||
|
installed_engines.clear();
|
||||||
|
QStringList params;
|
||||||
|
// Ask nova core for the supported search engines
|
||||||
|
QProcess nova;
|
||||||
|
params << misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py";
|
||||||
|
params << "--supported_engines";
|
||||||
|
nova.start("python", params, QIODevice::ReadOnly);
|
||||||
|
nova.waitForStarted();
|
||||||
|
nova.waitForFinished();
|
||||||
|
QByteArray result = nova.readAll();
|
||||||
|
result = result.replace("\r", "");
|
||||||
|
result = result.replace("\n", "");
|
||||||
|
qDebug("read: %s", result.data());
|
||||||
|
QByteArray e;
|
||||||
|
QStringList supported_engines_ids;
|
||||||
|
foreach(e, result.split(',')) {
|
||||||
|
QString en = QString(e);
|
||||||
|
supported_engines_ids << en;
|
||||||
|
installed_engines[en] = old_engines.value(en, true);
|
||||||
|
}
|
||||||
|
params.clear();
|
||||||
|
params << misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py";
|
||||||
|
params << "--supported_engines_infos";
|
||||||
|
nova.start("python", params, QIODevice::ReadOnly);
|
||||||
|
nova.waitForStarted();
|
||||||
|
nova.waitForFinished();
|
||||||
|
result = nova.readAll();
|
||||||
|
result = result.replace("\r", "");
|
||||||
|
result = result.replace("\n", "");
|
||||||
|
qDebug("read: %s", result.data());
|
||||||
|
unsigned int i = 0;
|
||||||
|
foreach(e, result.split(',')) {
|
||||||
|
QString id = supported_engines_ids.at(i);
|
||||||
|
QString nameUrlCouple(e);
|
||||||
|
QStringList line = nameUrlCouple.split('|');
|
||||||
|
if(line.size() != 2) continue;
|
||||||
|
QString enabledTxt;
|
||||||
|
if(installed_engines.value(id, true)) {
|
||||||
|
enabledTxt = tr("True");
|
||||||
|
} else {
|
||||||
|
enabledTxt = tr("False");
|
||||||
|
}
|
||||||
|
line << enabledTxt;
|
||||||
|
line << id;
|
||||||
|
QTreeWidgetItem *item = new QTreeWidgetItem(pluginsTree, line);
|
||||||
|
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".png";
|
||||||
|
if(QFile::exists(iconPath)) {
|
||||||
|
// Good, we already have the icon
|
||||||
|
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||||
|
} else {
|
||||||
|
// Icon is missing, we must download it
|
||||||
|
downloader->downloadUrl(line.at(1)+"/favicon.ico");
|
||||||
|
}
|
||||||
|
if(installed_engines.value(id, true))
|
||||||
|
setRowColor(i, "green");
|
||||||
|
else
|
||||||
|
setRowColor(i, "red");
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QTreeWidgetItem*> engineSelectDlg::findItemsWithUrl(QString url){
|
||||||
|
QList<QTreeWidgetItem*> res;
|
||||||
|
for(int i=0; i<pluginsTree->topLevelItemCount(); ++i) {
|
||||||
|
QTreeWidgetItem *item = pluginsTree->topLevelItem(i);
|
||||||
|
if(url.startsWith(item->text(ENGINE_URL), Qt::CaseInsensitive))
|
||||||
|
res << item;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeWidgetItem* engineSelectDlg::findItemWithID(QString id){
|
||||||
|
QList<QTreeWidgetItem*> res;
|
||||||
|
for(int i=0; i<pluginsTree->topLevelItemCount(); ++i) {
|
||||||
|
QTreeWidgetItem *item = pluginsTree->topLevelItem(i);
|
||||||
|
if(id == item->text(ENGINE_ID))
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool engineSelectDlg::isUpdateNeeded(QString plugin_name, float new_version) const {
|
||||||
|
float old_version = misc::getPluginVersion(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py");
|
||||||
|
qDebug("IsUpdate needed? tobeinstalled: %.2f, alreadyinstalled: %.2f", new_version, old_version);
|
||||||
|
return (new_version > old_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_ZZIP
|
||||||
|
void engineSelectDlg::installZipPlugin(QString path) {
|
||||||
|
QStringList plugins;
|
||||||
|
QStringList favicons;
|
||||||
|
ZZIP_DIR* dir = zzip_dir_open(path.toUtf8().data(), 0);
|
||||||
|
if(!dir) {
|
||||||
|
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("Search engine plugin archive could not be read."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ZZIP_DIRENT dirent;
|
||||||
|
while(zzip_dir_read(dir, &dirent)) {
|
||||||
|
/* show info for first file */
|
||||||
|
QString name(dirent.d_name);
|
||||||
|
if(name.endsWith(".py", Qt::CaseInsensitive)) {
|
||||||
|
plugins << name;
|
||||||
|
} else {
|
||||||
|
if(name.endsWith(".png", Qt::CaseInsensitive)) {
|
||||||
|
favicons << name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString plugin;
|
||||||
|
std::cout << dirent.d_name << std::endl;
|
||||||
|
ZZIP_FILE* fp = zzip_file_open(dir, dirent.d_name, 0);
|
||||||
|
if (fp) {
|
||||||
|
char buf[10];
|
||||||
|
zzip_ssize_t len = zzip_file_read(fp, buf, 10);
|
||||||
|
if (len) {
|
||||||
|
/* show head of README */
|
||||||
|
std::cout << buf;
|
||||||
|
}
|
||||||
|
zzip_file_close(fp);
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
foreach(plugin, plugins) {
|
||||||
|
QString plugin_name = plugin.split(QDir::separator()).last();
|
||||||
|
plugin_name.chop(3); // Remove .py extension
|
||||||
|
qDebug("Detected plugin %s in archive", plugin_name.toUtf8().data());
|
||||||
|
ZZIP_FILE* fp = zzip_file_open(dir, plugin.toUtf8().data(), 0);
|
||||||
|
if(fp) {
|
||||||
|
QTemporaryFile *tmpfile = new QTemporaryFile();
|
||||||
|
QString tmpPath;
|
||||||
|
// Write file
|
||||||
|
if(tmpfile->open()) {
|
||||||
|
tmpPath = tmpfile->fileName();
|
||||||
|
char buf[255];
|
||||||
|
zzip_ssize_t len = zzip_file_read(fp, buf, 255);
|
||||||
|
while(len) {
|
||||||
|
tmpfile->write(buf, len);
|
||||||
|
len = zzip_file_read(fp, buf, 255);
|
||||||
|
}
|
||||||
|
zzip_file_close(fp);
|
||||||
|
tmpfile->close();
|
||||||
|
} else {
|
||||||
|
qDebug("Could not open tmp file");
|
||||||
|
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||||
|
delete tmpfile;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Install plugin
|
||||||
|
installPlugin(tmpPath, plugin_name);
|
||||||
|
qDebug("installPlugin() finished");
|
||||||
|
delete tmpfile;
|
||||||
|
qDebug("Deleted tmpfile");
|
||||||
|
} else {
|
||||||
|
qDebug("Cannot read file in archive");
|
||||||
|
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString favicon;
|
||||||
|
foreach(favicon, favicons) {
|
||||||
|
qDebug("Detected favicon %s in archive", favicon.toUtf8().data());
|
||||||
|
// Ok we have a favicon here
|
||||||
|
QString plugin_name = favicon.split(QDir::separator()).last();
|
||||||
|
plugin_name.chop(4); // Remove .png extension
|
||||||
|
if(!QFile::exists(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py"))
|
||||||
|
continue;
|
||||||
|
// Check if we already have a favicon for this plugin
|
||||||
|
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".png";
|
||||||
|
if(QFile::exists(iconPath)) continue;
|
||||||
|
ZZIP_FILE* fp = zzip_file_open(dir, favicon.toUtf8().data(), 0);
|
||||||
|
if(fp) {
|
||||||
|
QFile dest_icon(iconPath);
|
||||||
|
// Write icon
|
||||||
|
if(dest_icon.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||||
|
char buf[255];
|
||||||
|
zzip_ssize_t len = zzip_file_read(fp, buf, 255);
|
||||||
|
while(len) {
|
||||||
|
dest_icon.write(buf, len);
|
||||||
|
len = zzip_file_read(fp, buf, 255);
|
||||||
|
}
|
||||||
|
zzip_file_close(fp);
|
||||||
|
dest_icon.close();
|
||||||
|
// Update icon in list
|
||||||
|
QTreeWidgetItem *item = findItemWithID(plugin_name);
|
||||||
|
Q_ASSERT(item);
|
||||||
|
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zzip_dir_close(dir);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
|
||||||
|
qDebug("Asked to install plugin at %s", path.toUtf8().data());
|
||||||
|
float new_version = misc::getPluginVersion(path);
|
||||||
|
qDebug("Version to be installed: %.2f", new_version);
|
||||||
|
if(!isUpdateNeeded(plugin_name, new_version)) {
|
||||||
|
qDebug("Apparently update it not needed, we have a more recent version");
|
||||||
|
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("A more recent version of %1 search engine plugin is already installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Process with install
|
||||||
|
QString dest_path = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py";
|
||||||
|
bool update = false;
|
||||||
|
if(QFile::exists(dest_path)) {
|
||||||
|
// Backup in case install fails
|
||||||
|
QFile::copy(dest_path, dest_path+".bak");
|
||||||
|
QFile::remove(dest_path);
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
// Copy the plugin
|
||||||
|
QFile::copy(path, dest_path);
|
||||||
|
// Check if this was correctly installed
|
||||||
|
if(!checkInstalled(plugin_name)) {
|
||||||
|
if(update) {
|
||||||
|
// Remove broken file
|
||||||
|
QFile::remove(dest_path);
|
||||||
|
// restore backup
|
||||||
|
QFile::copy(dest_path+".bak", dest_path);
|
||||||
|
QFile::remove(dest_path+".bak");
|
||||||
|
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be updated, keeping old version.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Remove broken file
|
||||||
|
QFile::remove(dest_path);
|
||||||
|
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Install was successful, remove backup
|
||||||
|
if(update) {
|
||||||
|
QFile::remove(dest_path+".bak");
|
||||||
|
}
|
||||||
|
// Refresh plugin list
|
||||||
|
loadSupportedSearchEngines();
|
||||||
|
if(update) {
|
||||||
|
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully updated.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully installed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::on_installButton_clicked() {
|
||||||
|
pluginSourceDlg *dlg = new pluginSourceDlg(this);
|
||||||
|
connect(dlg, SIGNAL(askForLocalFile()), this, SLOT(askForLocalPlugin()));
|
||||||
|
connect(dlg, SIGNAL(askForUrl()), this, SLOT(askForPluginUrl()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::askForPluginUrl() {
|
||||||
|
bool ok;
|
||||||
|
QString url = QInputDialog::getText(this, tr("New search engine plugin URL"),
|
||||||
|
tr("URL:"), QLineEdit::Normal,
|
||||||
|
"http://", &ok);
|
||||||
|
if (ok && !url.isEmpty())
|
||||||
|
downloader->downloadUrl(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::askForLocalPlugin() {
|
||||||
|
QStringList pathsList = QFileDialog::getOpenFileNames(0,
|
||||||
|
tr("Select search plugins"), QDir::homePath(),
|
||||||
|
#ifdef HAVE_ZZIP
|
||||||
|
tr("qBittorrent search plugins")+QString::fromUtf8(" (*.py *.zip)"));
|
||||||
|
#else
|
||||||
|
tr("qBittorrent search plugins")+QString::fromUtf8(" (*.py)"));
|
||||||
|
#endif
|
||||||
|
QString path;
|
||||||
|
foreach(path, pathsList) {
|
||||||
|
if(path.endsWith(".py", Qt::CaseInsensitive)) {
|
||||||
|
QString plugin_name = path.split(QDir::separator()).last();
|
||||||
|
plugin_name.replace(".py", "", Qt::CaseInsensitive);
|
||||||
|
installPlugin(path, plugin_name);
|
||||||
|
}
|
||||||
|
#ifdef HAVE_ZZIP
|
||||||
|
else {
|
||||||
|
if(path.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||||
|
installZipPlugin(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool engineSelectDlg::parseVersionsFile(QString versions_file, QString updateServer) {
|
||||||
|
qDebug("Checking if update is needed");
|
||||||
|
bool file_correct = false;
|
||||||
|
QFile versions(versions_file);
|
||||||
|
if(!versions.open(QIODevice::ReadOnly | QIODevice::Text)){
|
||||||
|
qDebug("* Error: Could not read versions.txt file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool updated = false;
|
||||||
|
while(!versions.atEnd()) {
|
||||||
|
QByteArray line = versions.readLine();
|
||||||
|
line.replace("\n", "");
|
||||||
|
line = line.trimmed();
|
||||||
|
if(line.isEmpty()) continue;
|
||||||
|
if(line.startsWith("#")) continue;
|
||||||
|
QList<QByteArray> list = line.split(' ');
|
||||||
|
if(list.size() != 2) continue;
|
||||||
|
QString plugin_name = QString(list.first());
|
||||||
|
if(!plugin_name.endsWith(":")) continue;
|
||||||
|
plugin_name.chop(1); // remove trailing ':'
|
||||||
|
bool ok;
|
||||||
|
float version = list.last().toFloat(&ok);
|
||||||
|
qDebug("read line %s: %.2f", plugin_name.toUtf8().data(), version);
|
||||||
|
if(!ok) continue;
|
||||||
|
file_correct = true;
|
||||||
|
if(isUpdateNeeded(plugin_name, version)) {
|
||||||
|
qDebug("Plugin: %s is outdated", plugin_name.toUtf8().data());
|
||||||
|
// Downloading update
|
||||||
|
downloader->downloadUrl(updateServer+plugin_name+".pyqBT"); // Actually this is really a .py
|
||||||
|
downloader->downloadUrl(updateServer+plugin_name+".png");
|
||||||
|
updated = true;
|
||||||
|
}else {
|
||||||
|
qDebug("Plugin: %s is up to date", plugin_name.toUtf8().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Close file
|
||||||
|
versions.close();
|
||||||
|
// Clean up tmp file
|
||||||
|
QFile::remove(versions_file);
|
||||||
|
if(file_correct && !updated) {
|
||||||
|
QMessageBox::information(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("All your plugins are already up to date."));
|
||||||
|
}
|
||||||
|
return file_correct;
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
|
||||||
|
qDebug("engineSelectDlg received %s", url.toUtf8().data());
|
||||||
|
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
|
||||||
|
// Icon downloaded
|
||||||
|
QImage fileIcon;
|
||||||
|
#ifdef HAVE_MAGICK
|
||||||
|
try{
|
||||||
|
QFile::copy(filePath, filePath+".ico");
|
||||||
|
Image image(QDir::cleanPath(filePath+".ico").toUtf8().data());
|
||||||
|
// Convert to PNG since we can't read ICO format
|
||||||
|
image.magick("PNG");
|
||||||
|
// Resize to 16x16px
|
||||||
|
image.sample(Geometry(16, 16));
|
||||||
|
image.write(filePath.toUtf8().data());
|
||||||
|
QFile::remove(filePath+".ico");
|
||||||
|
}catch(Magick::Exception &error_){
|
||||||
|
qDebug("favicon conversion to PNG failure: %s", error_.what());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if(fileIcon.load(filePath)) {
|
||||||
|
QList<QTreeWidgetItem*> items = findItemsWithUrl(url);
|
||||||
|
QTreeWidgetItem *item;
|
||||||
|
foreach(item, items){
|
||||||
|
QString id = item->text(ENGINE_ID);
|
||||||
|
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".png";
|
||||||
|
QFile::copy(filePath, iconPath);
|
||||||
|
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Delete tmp file
|
||||||
|
QFile::remove(filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(url == "http://www.dchris.eu/search_engine/versions.txt") {
|
||||||
|
if(!parseVersionsFile(filePath, "http://www.dchris.eu/search_engine/")) {
|
||||||
|
qDebug("Primary update server failed, try secondary");
|
||||||
|
downloader->downloadUrl("http://hydr0g3n.free.fr/search_engine/versions.txt");
|
||||||
|
}
|
||||||
|
QFile::remove(filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(url == "http://hydr0g3n.free.fr/search_engine/versions.txt") {
|
||||||
|
if(!parseVersionsFile(filePath, "http://hydr0g3n.free.fr/search_engine/")) {
|
||||||
|
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
|
||||||
|
}
|
||||||
|
QFile::remove(filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(url.endsWith(".pyqBT", Qt::CaseInsensitive) || url.endsWith(".py", Qt::CaseInsensitive)) {
|
||||||
|
QString plugin_name = url.split('/').last();
|
||||||
|
plugin_name.replace(".pyqBT", "");
|
||||||
|
plugin_name.replace(".py", "");
|
||||||
|
installPlugin(filePath, plugin_name);
|
||||||
|
QFile::remove(filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef HAVE_ZZIP
|
||||||
|
if(url.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||||
|
installZipPlugin(filePath);
|
||||||
|
QFile::remove(filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void engineSelectDlg::handleDownloadFailure(QString url, QString reason) {
|
||||||
|
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
|
||||||
|
qDebug("Could not download favicon: %s, reason: %s", url.toUtf8().data(), reason.toUtf8().data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(url == "http://www.dchris.eu/search_engine/versions.txt") {
|
||||||
|
// Primary update server failed, try secondary
|
||||||
|
qDebug("Primary update server failed, try secondary");
|
||||||
|
downloader->downloadUrl("http://hydr0g3n.free.fr/search_engine/versions.txt");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(url == "http://hydr0g3n.free.fr/search_engine/versions.txt") {
|
||||||
|
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(url.endsWith(".pyqBT", Qt::CaseInsensitive) || url.endsWith(".py", Qt::CaseInsensitive)) {
|
||||||
|
// a plugin update download has been failed
|
||||||
|
QString plugin_name = url.split('/').last();
|
||||||
|
plugin_name.replace(".pyqBT", "", Qt::CaseInsensitive);
|
||||||
|
plugin_name.replace(".py", "", Qt::CaseInsensitive);
|
||||||
|
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, %1 search plugin install failed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||||
|
}
|
||||||
|
#ifdef HAVE_ZZIP
|
||||||
|
if(url.endsWith(".zip", Qt::CaseInsensitive)) {
|
||||||
|
QString plugin_name = url.split('/').last();
|
||||||
|
plugin_name.replace(".zip", "", Qt::CaseInsensitive);
|
||||||
|
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, %1 search plugin install failed.", "%1 is the name of the search engine").arg(plugin_name.toUtf8().data()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
76
src/engineSelectDlg.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ENGINE_SELECT_DLG_H
|
||||||
|
#define ENGINE_SELECT_DLG_H
|
||||||
|
|
||||||
|
#include "ui_engineSelect.h"
|
||||||
|
|
||||||
|
class downloadThread;
|
||||||
|
class QDropEvent;
|
||||||
|
|
||||||
|
class engineSelectDlg : public QDialog, public Ui::engineSelect{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Search related
|
||||||
|
QHash<QString, bool> installed_engines;
|
||||||
|
downloadThread *downloader;
|
||||||
|
|
||||||
|
public:
|
||||||
|
engineSelectDlg(QWidget *parent);
|
||||||
|
~engineSelectDlg();
|
||||||
|
QList<QTreeWidgetItem*> findItemsWithUrl(QString url);
|
||||||
|
QTreeWidgetItem* findItemWithID(QString id);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool parseVersionsFile(QString versions_file, QString updateServer);
|
||||||
|
bool isUpdateNeeded(QString plugin_name, float new_version) const;
|
||||||
|
bool checkInstalled(QString plugin_name) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void enginesChanged();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void saveSettings();
|
||||||
|
void on_closeButton_clicked();
|
||||||
|
void loadSupportedSearchEngines(bool first=false);
|
||||||
|
void toggleEngineState(QTreeWidgetItem*, int);
|
||||||
|
void setRowColor(int row, QString color);
|
||||||
|
void processDownloadedFile(QString url, QString filePath);
|
||||||
|
void handleDownloadFailure(QString url, QString reason);
|
||||||
|
void displayContextMenu(const QPoint& pos);
|
||||||
|
void enableSelection();
|
||||||
|
void disableSelection();
|
||||||
|
void on_actionUninstall_triggered();
|
||||||
|
void on_updateButton_clicked();
|
||||||
|
void on_installButton_clicked();
|
||||||
|
void dropEvent(QDropEvent *event);
|
||||||
|
void dragEnterEvent(QDragEnterEvent *event);
|
||||||
|
void installPlugin(QString plugin_path, QString plugin_name);
|
||||||
|
void askForLocalPlugin();
|
||||||
|
void askForPluginUrl();
|
||||||
|
#ifdef HAVE_ZZIP
|
||||||
|
void installZipPlugin(QString path);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
186
src/eventmanager.cpp
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "eventmanager.h"
|
||||||
|
#include "json.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
EventManager::EventManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
revision = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::update(QVariantMap event)
|
||||||
|
{
|
||||||
|
revision++;
|
||||||
|
events << QPair<ulong, QVariantMap>(revision, event);
|
||||||
|
emit updated();
|
||||||
|
qDebug("Added the following event");
|
||||||
|
qDebug() << event;
|
||||||
|
/* QLinkedList<QPair<ulong, QVariantMap> >::iterator i;
|
||||||
|
for (i = events.begin(); i != events.end(); i++)
|
||||||
|
qDebug() << *i;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant EventManager::querySince(ulong r) const
|
||||||
|
{
|
||||||
|
QVariantList list;
|
||||||
|
QLinkedListIterator<QPair<ulong, QVariantMap> > i(events);
|
||||||
|
i.toBack();
|
||||||
|
while (i.hasPrevious())
|
||||||
|
{
|
||||||
|
QPair<ulong, QVariantMap> pair = i.previous();
|
||||||
|
if (pair.first <= r)
|
||||||
|
break;
|
||||||
|
list.prepend(QVariant(pair.second));
|
||||||
|
}
|
||||||
|
QVariantMap map;
|
||||||
|
map["events"] = QVariant(list);
|
||||||
|
map["revision"] = QVariant((qulonglong) revision);
|
||||||
|
return QVariant(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventManager::isUpdated(ulong r) const
|
||||||
|
{
|
||||||
|
return (r < revision);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::addedTorrent(QString, QTorrentHandle& h)
|
||||||
|
{
|
||||||
|
QVariantMap event;
|
||||||
|
event["type"] = QVariant("add");
|
||||||
|
event["hash"] = QVariant(h.hash());
|
||||||
|
event["name"] = QVariant(h.name());
|
||||||
|
update(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::deletedTorrent(QString hash)
|
||||||
|
{
|
||||||
|
QVariantMap event;
|
||||||
|
event["type"] = QVariant("delete");
|
||||||
|
event["hash"] = QVariant(hash);
|
||||||
|
QLinkedList<QPair<ulong, QVariantMap> >::iterator i = events.end();
|
||||||
|
bool loop = true;
|
||||||
|
while (loop && i != events.begin()) {
|
||||||
|
--i;
|
||||||
|
QVariantMap oldevent = i->second;
|
||||||
|
if(oldevent["hash"] == QVariant(hash))
|
||||||
|
{
|
||||||
|
if(oldevent["type"] == QVariant("add"))
|
||||||
|
loop = false;
|
||||||
|
i = events.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::modifiedTorrent(QTorrentHandle h)
|
||||||
|
{
|
||||||
|
QString hash = h.hash();
|
||||||
|
QVariantMap event;
|
||||||
|
QVariant v;
|
||||||
|
|
||||||
|
if(h.is_paused())
|
||||||
|
v = QVariant("paused");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(h.state())
|
||||||
|
{
|
||||||
|
case torrent_status::finished:
|
||||||
|
case torrent_status::seeding:
|
||||||
|
v = QVariant("seeding");
|
||||||
|
break;
|
||||||
|
case torrent_status::checking_files:
|
||||||
|
case torrent_status::queued_for_checking:
|
||||||
|
v = QVariant("checking");
|
||||||
|
break;
|
||||||
|
case torrent_status::connecting_to_tracker:
|
||||||
|
if(h.download_payload_rate() > 0)
|
||||||
|
v = QVariant("downloading");
|
||||||
|
else
|
||||||
|
v = QVariant("connecting");
|
||||||
|
break;
|
||||||
|
case torrent_status::downloading:
|
||||||
|
case torrent_status::downloading_metadata:
|
||||||
|
if(h.download_payload_rate() > 0)
|
||||||
|
v = QVariant("downloading");
|
||||||
|
else
|
||||||
|
v = QVariant("stalled");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
v = QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(modify(hash, "state", v))
|
||||||
|
event["state"] = v;
|
||||||
|
|
||||||
|
v = QVariant((qlonglong)h.actual_size());
|
||||||
|
if(modify(hash, "size", v))
|
||||||
|
event["size"] = v;
|
||||||
|
|
||||||
|
v = QVariant(h.progress());
|
||||||
|
if(modify(hash, "progress", v))
|
||||||
|
event["progress"] = v;
|
||||||
|
|
||||||
|
v = QVariant(h.download_payload_rate());
|
||||||
|
if(modify(hash, "dlspeed", v))
|
||||||
|
event["dlspeed"] = v;
|
||||||
|
|
||||||
|
v = QVariant(h.upload_payload_rate());
|
||||||
|
if(modify(hash, "upspeed", v))
|
||||||
|
event["upspeed"] = v;
|
||||||
|
|
||||||
|
if(event.size() > 0)
|
||||||
|
{
|
||||||
|
event["type"] = QVariant("modify");
|
||||||
|
event["hash"] = QVariant(hash);
|
||||||
|
update(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EventManager::modify(QString hash, QString key, QVariant value)
|
||||||
|
{
|
||||||
|
QLinkedList<QPair<ulong, QVariantMap> >::iterator i = events.end();
|
||||||
|
while (i != events.begin()) {
|
||||||
|
--i;
|
||||||
|
QVariantMap event = i->second;
|
||||||
|
if(event["hash"] == QVariant(hash))
|
||||||
|
{
|
||||||
|
if(event["type"] == QVariant("add"))
|
||||||
|
return true;
|
||||||
|
if(event.contains(key))
|
||||||
|
{
|
||||||
|
if(event[key] == value)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(event.size() <= 3)
|
||||||
|
i = events.erase(i);
|
||||||
|
else
|
||||||
|
i->second.remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
55
src/eventmanager.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef EVENTMANAGER_H
|
||||||
|
#define EVENTMANAGER_H
|
||||||
|
|
||||||
|
#include "qtorrenthandle.h"
|
||||||
|
#include <QLinkedList>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
class EventManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
ulong revision;
|
||||||
|
QLinkedList<QPair <ulong, QVariantMap> > events;
|
||||||
|
bool modify(QString hash, QString key, QVariant value);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void update(QVariantMap event);
|
||||||
|
|
||||||
|
public:
|
||||||
|
EventManager(QObject *parent = 0);
|
||||||
|
QVariant querySince(ulong r) const;
|
||||||
|
bool isUpdated(ulong r) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void updated();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void addedTorrent(QString path, QTorrentHandle& h);
|
||||||
|
void deletedTorrent(QString hash);
|
||||||
|
void modifiedTorrent(QTorrentHandle h);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
383
src/filterParserThread.h
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FILTERPARSERTHREAD_H
|
||||||
|
#define FILTERPARSERTHREAD_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include <libtorrent/session.hpp>
|
||||||
|
#include <libtorrent/ip_filter.hpp>
|
||||||
|
|
||||||
|
using namespace libtorrent;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// P2B Stuff
|
||||||
|
#include <string.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
// End of P2B stuff
|
||||||
|
|
||||||
|
class FilterParserThread : public QThread {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
session *s;
|
||||||
|
ip_filter filter;
|
||||||
|
bool abort;
|
||||||
|
QString filePath;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run(){
|
||||||
|
qDebug("Processing filter file");
|
||||||
|
if(filePath.endsWith(".dat", Qt::CaseInsensitive)) {
|
||||||
|
// eMule DAT file
|
||||||
|
parseDATFilterFile(filePath);
|
||||||
|
} else {
|
||||||
|
if(filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
|
||||||
|
// PeerGuardian p2p file
|
||||||
|
parseP2PFilterFile(filePath);
|
||||||
|
} else {
|
||||||
|
if(filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
|
||||||
|
// PeerGuardian p2p file
|
||||||
|
parseP2BFilterFile(filePath);
|
||||||
|
} else {
|
||||||
|
// Default: eMule DAT format
|
||||||
|
parseDATFilterFile(filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s->set_ip_filter(filter);
|
||||||
|
qDebug("IP Filter thread: finished parsing, filter applied");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FilterParserThread(QObject* parent, session *s) : QThread(parent), s(s), abort(false) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~FilterParserThread(){
|
||||||
|
abort = true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for eMule ip filter in DAT format
|
||||||
|
void parseDATFilterFile(QString filePath) {
|
||||||
|
const QRegExp is_ipv6(QString::fromUtf8("^[0-9a-f]{4}(:[0-9a-f]{4}){7}$"), Qt::CaseInsensitive, QRegExp::RegExp);
|
||||||
|
const QRegExp is_ipv4(QString::fromUtf8("^(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))(\\.(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))){3}$"), Qt::CaseInsensitive, QRegExp::RegExp);
|
||||||
|
QString strStartIP, strEndIP;
|
||||||
|
bool IPv4 = true;
|
||||||
|
QFile file(filePath);
|
||||||
|
if (file.exists()){
|
||||||
|
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("Couldn't open %1 in read mode.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned int nbLine = 0;
|
||||||
|
while (!file.atEnd() && !abort) {
|
||||||
|
++nbLine;
|
||||||
|
QByteArray line = file.readLine();
|
||||||
|
// Ignoring empty lines
|
||||||
|
line = line.trimmed();
|
||||||
|
if(line.isEmpty()) continue;
|
||||||
|
// Ignoring commented lines
|
||||||
|
if(line.startsWith('#') || line.startsWith("//")) continue;
|
||||||
|
// Line is not commented
|
||||||
|
QList<QByteArray> partsList = line.split(',');
|
||||||
|
unsigned int nbElem = partsList.size();
|
||||||
|
// IP Range can be splitted by a dash or a comma...
|
||||||
|
// Check if there is a dash in first part
|
||||||
|
QByteArray firstPart = partsList.at(0);
|
||||||
|
int nbAccess = 0;
|
||||||
|
if(firstPart.contains('-')) {
|
||||||
|
// Range is splitted by a dash
|
||||||
|
QList<QByteArray> IPs = firstPart.split('-');
|
||||||
|
if(IPs.size() != 2) {
|
||||||
|
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
strStartIP = IPs.at(0).trimmed();
|
||||||
|
strEndIP = IPs.at(1).trimmed();
|
||||||
|
// Check if IPs are correct
|
||||||
|
if(strStartIP.contains(is_ipv4) && strEndIP.contains(is_ipv4)) {
|
||||||
|
IPv4 = true;
|
||||||
|
} else {
|
||||||
|
if(strStartIP.contains(is_ipv6) && strEndIP.contains(is_ipv6)) {
|
||||||
|
IPv4 = false;
|
||||||
|
} else {
|
||||||
|
// Could not determine IP format
|
||||||
|
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if there is an access value (apparently not mandatory)
|
||||||
|
if(nbElem > 1) {
|
||||||
|
// There is possibly one
|
||||||
|
bool ok;
|
||||||
|
nbAccess = partsList.at(1).trimmed().toInt(&ok);
|
||||||
|
if(!ok){
|
||||||
|
nbAccess = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Range is probably splitted by a comma
|
||||||
|
unsigned int nbElem = partsList.size();
|
||||||
|
if(nbElem > 1) {
|
||||||
|
strStartIP = firstPart.trimmed();
|
||||||
|
strEndIP = partsList.at(1).trimmed();
|
||||||
|
// Check if IPs are correct
|
||||||
|
if(strStartIP.contains(is_ipv4) && strEndIP.contains(is_ipv4)) {
|
||||||
|
IPv4 = true;
|
||||||
|
} else {
|
||||||
|
if(strStartIP.contains(is_ipv6) && strEndIP.contains(is_ipv6)) {
|
||||||
|
IPv4 = false;
|
||||||
|
} else {
|
||||||
|
// Could not determine IP format
|
||||||
|
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if there is an access value (apparently not mandatory)
|
||||||
|
if(nbElem > 2) {
|
||||||
|
// There is possibly one
|
||||||
|
bool ok;
|
||||||
|
nbAccess = partsList.at(2).trimmed().toInt(&ok);
|
||||||
|
if(!ok){
|
||||||
|
nbAccess = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(nbAccess > 127) {
|
||||||
|
// Ignoring this rule because access value is too high
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Now Add to the filter
|
||||||
|
QStringList IP;
|
||||||
|
if(IPv4) {
|
||||||
|
//IPv4 addresses
|
||||||
|
IP = strStartIP.split('.');
|
||||||
|
address_v4 start((IP.at(0).toInt() << 24) + (IP.at(1).toInt() << 16) + (IP.at(2).toInt() << 8) + IP.at(3).toInt());
|
||||||
|
IP = strEndIP.split('.');
|
||||||
|
address_v4 last((IP.at(0).toInt() << 24) + (IP.at(1).toInt() << 16) + (IP.at(2).toInt() << 8) + IP.at(3).toInt());
|
||||||
|
// Apply to bittorrent session
|
||||||
|
filter.add_rule(start, last, ip_filter::blocked);
|
||||||
|
} else {
|
||||||
|
// IPv6, ex : 1fff:0000:0a88:85a3:0000:0000:ac1f:8001
|
||||||
|
IP = strStartIP.split(':');
|
||||||
|
address_v6 start = address_v6::from_string(strStartIP.remove(':', 0).toUtf8().data());
|
||||||
|
IP = strEndIP.split(':');
|
||||||
|
address_v6 last = address_v6::from_string(strEndIP.remove(':', 0).toUtf8().data());
|
||||||
|
// Apply to bittorrent session
|
||||||
|
filter.add_rule(start, last, ip_filter::blocked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for PeerGuardian ip filter in p2p format
|
||||||
|
void parseP2PFilterFile(QString filePath) {
|
||||||
|
const QRegExp is_ipv4(QString::fromUtf8("^(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))(\\.(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))){3}$"), Qt::CaseInsensitive, QRegExp::RegExp);
|
||||||
|
QFile file(filePath);
|
||||||
|
QStringList IP;
|
||||||
|
if (file.exists()){
|
||||||
|
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("Couldn't open %1 in read mode.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned int nbLine = 0;
|
||||||
|
while (!file.atEnd() && !abort) {
|
||||||
|
++nbLine;
|
||||||
|
QByteArray line = file.readLine();
|
||||||
|
// Ignoring empty lines
|
||||||
|
line = line.trimmed();
|
||||||
|
if(line.isEmpty()) continue;
|
||||||
|
// Ignoring commented lines
|
||||||
|
if(line.startsWith('#') || line.startsWith("//")) continue;
|
||||||
|
// Line is not commented
|
||||||
|
QList<QByteArray> partsList = line.split(':');
|
||||||
|
if(partsList.size() < 2){
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Get IP range
|
||||||
|
QList<QByteArray> IPs = partsList.last().split('-');
|
||||||
|
if(IPs.size() != 2) {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QString strStartIP = IPs.at(0).trimmed();
|
||||||
|
QString strEndIP = IPs.at(1).trimmed();
|
||||||
|
// Check IPs format (IPv4 only)
|
||||||
|
if(strStartIP.contains(is_ipv4) && strEndIP.contains(is_ipv4)) {
|
||||||
|
// IPv4
|
||||||
|
IP = strStartIP.split('.');
|
||||||
|
address_v4 start((IP.at(0).toInt() << 24) + (IP.at(1).toInt() << 16) + (IP.at(2).toInt() << 8) + IP.at(3).toInt());
|
||||||
|
IP = strEndIP.split('.');
|
||||||
|
address_v4 last((IP.at(0).toInt() << 24) + (IP.at(1).toInt() << 16) + (IP.at(2).toInt() << 8) + IP.at(3).toInt());
|
||||||
|
// Apply to bittorrent session
|
||||||
|
filter.add_rule(start, last, ip_filter::blocked);
|
||||||
|
} else {
|
||||||
|
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getlineInStream(QDataStream& stream, string& name, char delim) {
|
||||||
|
char c;
|
||||||
|
int total_read = 0;
|
||||||
|
do {
|
||||||
|
int read = stream.readRawData(&c, 1);
|
||||||
|
total_read += read;
|
||||||
|
if(read > 0) {
|
||||||
|
if(c != delim) {
|
||||||
|
name += c;
|
||||||
|
} else {
|
||||||
|
// Delim found
|
||||||
|
return total_read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(read > 0);
|
||||||
|
return total_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parser for PeerGuardian ip filter in p2p format
|
||||||
|
void parseP2BFilterFile(QString filePath) {
|
||||||
|
QFile file(filePath);
|
||||||
|
if (file.exists()){
|
||||||
|
if(!file.open(QIODevice::ReadOnly)){
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("Couldn't open %1 in read mode.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QDataStream stream(&file);
|
||||||
|
// Read header
|
||||||
|
char buf[7];
|
||||||
|
unsigned char version;
|
||||||
|
if(
|
||||||
|
!stream.readRawData(buf, sizeof(buf)) ||
|
||||||
|
memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) ||
|
||||||
|
!stream.readRawData((char*)&version, sizeof(version))
|
||||||
|
) {
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(version==1 || version==2) {
|
||||||
|
qDebug ("p2b version 1 or 2");
|
||||||
|
unsigned int start, end;
|
||||||
|
|
||||||
|
string name;
|
||||||
|
while(getlineInStream(stream, name, '\0') && !abort) {
|
||||||
|
if(
|
||||||
|
!stream.readRawData((char*)&start, sizeof(start)) ||
|
||||||
|
!stream.readRawData((char*)&end, sizeof(end))
|
||||||
|
) {
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Network byte order to Host byte order
|
||||||
|
// asio address_v4 contructor expects it
|
||||||
|
// that way
|
||||||
|
address_v4 first(ntohl(start));
|
||||||
|
address_v4 last(ntohl(end));
|
||||||
|
// Apply to bittorrent session
|
||||||
|
filter.add_rule(first, last, ip_filter::blocked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(version==3) {
|
||||||
|
qDebug ("p2b version 3");
|
||||||
|
unsigned int namecount;
|
||||||
|
if(!stream.readRawData((char*)&namecount, sizeof(namecount))) {
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
namecount=ntohl(namecount);
|
||||||
|
// Reading names although, we don't really care about them
|
||||||
|
for(unsigned int i=0; i<namecount; i++) {
|
||||||
|
string name;
|
||||||
|
if(!getlineInStream(stream, name, '\0')) {
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(abort) return;
|
||||||
|
}
|
||||||
|
// Reading the ranges
|
||||||
|
unsigned int rangecount;
|
||||||
|
if(!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rangecount=ntohl(rangecount);
|
||||||
|
|
||||||
|
unsigned int name, start, end;
|
||||||
|
|
||||||
|
for(unsigned int i=0; i<rangecount; i++) {
|
||||||
|
if(
|
||||||
|
!stream.readRawData((char*)&name, sizeof(name)) ||
|
||||||
|
!stream.readRawData((char*)&start, sizeof(start)) ||
|
||||||
|
!stream.readRawData((char*)&end, sizeof(end))
|
||||||
|
) {
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Network byte order to Host byte order
|
||||||
|
// asio address_v4 contructor expects it
|
||||||
|
// that way
|
||||||
|
address_v4 first(ntohl(start));
|
||||||
|
address_v4 last(ntohl(end));
|
||||||
|
// Apply to bittorrent session
|
||||||
|
filter.add_rule(first, last, ip_filter::blocked);
|
||||||
|
if(abort) return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process ip filter file
|
||||||
|
// Supported formats:
|
||||||
|
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
|
||||||
|
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
|
||||||
|
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
|
||||||
|
void processFilterFile(QString _filePath){
|
||||||
|
if(isRunning()) {
|
||||||
|
// Already parsing a filter, abort first
|
||||||
|
abort = true;
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
abort = false;
|
||||||
|
filePath = _filePath;
|
||||||
|
// Run it
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
204
src/httpconnection.cpp
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "httpconnection.h"
|
||||||
|
#include "httpserver.h"
|
||||||
|
#include "eventmanager.h"
|
||||||
|
#include "json.h"
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QHttpRequestHeader>
|
||||||
|
#include <QHttpResponseHeader>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
HttpConnection::HttpConnection(QTcpSocket *socket, HttpServer *parent)
|
||||||
|
: QObject(parent), socket(socket), parent(parent)
|
||||||
|
{
|
||||||
|
socket->setParent(this);
|
||||||
|
connect(socket, SIGNAL(readyRead()), this, SLOT(read()));
|
||||||
|
connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater()));
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpConnection::~HttpConnection()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::read()
|
||||||
|
{
|
||||||
|
QString input = socket->readAll();
|
||||||
|
qDebug(" -------");
|
||||||
|
qDebug("|REQUEST|");
|
||||||
|
qDebug(" -------");
|
||||||
|
qDebug(input.toAscii().constData());
|
||||||
|
parser.write(input);
|
||||||
|
if(parser.isError())
|
||||||
|
{
|
||||||
|
generator.setStatusLine(400, "Bad Request");
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (parser.isParsable())
|
||||||
|
respond();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::write()
|
||||||
|
{
|
||||||
|
QByteArray output = generator.toByteArray();
|
||||||
|
qDebug(" --------");
|
||||||
|
qDebug("|RESPONSE|");
|
||||||
|
qDebug(" --------");
|
||||||
|
qDebug()<<output;
|
||||||
|
socket->write(output);
|
||||||
|
socket->disconnectFromHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::respond()
|
||||||
|
{
|
||||||
|
QStringList auth = parser.value("Authorization").split(" ", QString::SkipEmptyParts);
|
||||||
|
if (auth.size() != 2 || QString::compare(auth[0], "Basic", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth[1].toUtf8()))
|
||||||
|
{
|
||||||
|
generator.setStatusLine(401, "Unauthorized");
|
||||||
|
generator.setValue("WWW-Authenticate", "Basic realm=\"you know what\"");
|
||||||
|
write();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString url = parser.url();
|
||||||
|
QStringList list = url.split('/', QString::SkipEmptyParts);
|
||||||
|
if (list.contains(".") || list.contains(".."))
|
||||||
|
{
|
||||||
|
respondNotFound();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (list.size() == 0)
|
||||||
|
list.append("index.html");
|
||||||
|
if (list.size() == 2)
|
||||||
|
{
|
||||||
|
if (list[0] == "json")
|
||||||
|
{
|
||||||
|
if (list[1] == "events")
|
||||||
|
{
|
||||||
|
EventManager* manager = parent->eventManager();
|
||||||
|
uint r = parser.get("r").toUInt();
|
||||||
|
if(manager->isUpdated(r))
|
||||||
|
respondJson();
|
||||||
|
else
|
||||||
|
connect(manager, SIGNAL(updated()), this, SLOT(respondJson()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (list[0] == "command")
|
||||||
|
{
|
||||||
|
QString command = list[1];
|
||||||
|
respondCommand(command);
|
||||||
|
generator.setStatusLine(200, "OK");
|
||||||
|
write();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (list[0] == "images")
|
||||||
|
list[0] = "Icons";
|
||||||
|
else
|
||||||
|
list.prepend("webui");
|
||||||
|
url = ":/" + list.join("/");
|
||||||
|
QFile file(url);
|
||||||
|
if(!file.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
respondNotFound();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString ext = list.last();
|
||||||
|
int index = ext.lastIndexOf('.') + 1;
|
||||||
|
if (index > 0)
|
||||||
|
ext.remove(0, index);
|
||||||
|
else
|
||||||
|
ext.clear();
|
||||||
|
QByteArray data = file.readAll();
|
||||||
|
generator.setStatusLine(200, "OK");
|
||||||
|
generator.setContentTypeByExt(ext);
|
||||||
|
generator.setMessage(data);
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::respondNotFound()
|
||||||
|
{
|
||||||
|
generator.setStatusLine(404, "File not found");
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::respondJson()
|
||||||
|
{
|
||||||
|
EventManager* manager = parent->eventManager();
|
||||||
|
QString temp = parser.get("r");
|
||||||
|
uint r = parser.get("r").toUInt();
|
||||||
|
QVariant data = manager->querySince(r);
|
||||||
|
QString string = toJson(data);
|
||||||
|
generator.setStatusLine(200, "OK");
|
||||||
|
generator.setContentTypeByExt("js");
|
||||||
|
generator.setMessage(string);
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpConnection::respondCommand(QString command)
|
||||||
|
{
|
||||||
|
if(command == "download")
|
||||||
|
{
|
||||||
|
QString urls = parser.post("urls");
|
||||||
|
QStringList list = urls.split('\n');
|
||||||
|
QStringList url_list_cleaned;
|
||||||
|
foreach(QString url, list){
|
||||||
|
url = url.trimmed();
|
||||||
|
if(!url.isEmpty()){
|
||||||
|
if(url_list_cleaned.indexOf(QRegExp(url, Qt::CaseInsensitive, QRegExp::FixedString)) < 0){
|
||||||
|
url_list_cleaned << url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit urlsReadyToBeDownloaded(url_list_cleaned);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(command == "resumeall")
|
||||||
|
{
|
||||||
|
emit resumeAllTorrents();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(command == "pauseall")
|
||||||
|
{
|
||||||
|
emit pauseAllTorrents();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(command == "resume")
|
||||||
|
{
|
||||||
|
emit resumeTorrent(parser.post("hash"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(command == "pause")
|
||||||
|
{
|
||||||
|
emit pauseTorrent(parser.post("hash"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(command == "delete")
|
||||||
|
{
|
||||||
|
emit deleteTorrent(parser.post("hash"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/httpconnection.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HTTPCONNECTION_H
|
||||||
|
#define HTTPCONNECTION_H
|
||||||
|
|
||||||
|
#include "httprequestparser.h"
|
||||||
|
#include "httpresponsegenerator.h"
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QTcpSocket;
|
||||||
|
|
||||||
|
class HttpServer;
|
||||||
|
|
||||||
|
class HttpConnection : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
QTcpSocket *socket;
|
||||||
|
HttpServer *parent;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HttpRequestParser parser;
|
||||||
|
HttpResponseGenerator generator;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void write();
|
||||||
|
virtual void respond();
|
||||||
|
void respondJson();
|
||||||
|
void respondCommand(QString command);
|
||||||
|
void respondNotFound();
|
||||||
|
|
||||||
|
public:
|
||||||
|
HttpConnection(QTcpSocket *socket, HttpServer *parent);
|
||||||
|
~HttpConnection();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void read();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void urlsReadyToBeDownloaded(const QStringList&);
|
||||||
|
void deleteTorrent(QString hash);
|
||||||
|
void resumeTorrent(QString hash);
|
||||||
|
void pauseTorrent(QString hash);
|
||||||
|
void resumeAllTorrents();
|
||||||
|
void pauseAllTorrents();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
128
src/httprequestparser.cpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "httprequestparser.h"
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
HttpRequestParser::HttpRequestParser()
|
||||||
|
{
|
||||||
|
headerDone = false;
|
||||||
|
messageDone = false;
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestParser::~HttpRequestParser()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpRequestParser::isParsable() const
|
||||||
|
{
|
||||||
|
return !error && headerDone && isValid() && (messageDone || !hasContentLength() || contentLength() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpRequestParser::isError() const
|
||||||
|
{
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HttpRequestParser::url() const
|
||||||
|
{
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HttpRequestParser::message() const
|
||||||
|
{
|
||||||
|
if(isParsable())
|
||||||
|
return data;
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HttpRequestParser::get(const QString key) const
|
||||||
|
{
|
||||||
|
return getMap[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HttpRequestParser::post(const QString key) const
|
||||||
|
{
|
||||||
|
return postMap[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpRequestParser::write(QString str)
|
||||||
|
{
|
||||||
|
while (!headerDone && str.size()>0)
|
||||||
|
{
|
||||||
|
int index = str.indexOf('\n') + 1;
|
||||||
|
if(index == 0)
|
||||||
|
{
|
||||||
|
data += str;
|
||||||
|
str.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data += str.left(index);
|
||||||
|
str.remove(0, index);
|
||||||
|
if(data.right(4) == "\r\n\r\n")
|
||||||
|
{
|
||||||
|
QHttpRequestHeader::operator=(QHttpRequestHeader(data));
|
||||||
|
headerDone = true;
|
||||||
|
data.clear();
|
||||||
|
QUrl url = QUrl::fromEncoded(QHttpRequestHeader::path().toAscii());
|
||||||
|
path = url.path();
|
||||||
|
qDebug() << path;
|
||||||
|
QListIterator<QPair<QString, QString> > i(url.queryItems());
|
||||||
|
while (i.hasNext())
|
||||||
|
{
|
||||||
|
QPair<QString, QString> pair = i.next();
|
||||||
|
getMap[pair.first] = pair.second;
|
||||||
|
qDebug() << pair.first << "=" << get(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!messageDone && str.size()>0)
|
||||||
|
{
|
||||||
|
if(hasContentLength())
|
||||||
|
{
|
||||||
|
data += str;
|
||||||
|
if(data.size() >= (int) contentLength())
|
||||||
|
{
|
||||||
|
data.resize(contentLength());
|
||||||
|
messageDone = true;
|
||||||
|
//parse POST data
|
||||||
|
if(contentType() == "application/x-www-form-urlencoded")
|
||||||
|
{
|
||||||
|
QUrl url;
|
||||||
|
url.setEncodedQuery(data.toAscii());
|
||||||
|
QListIterator<QPair<QString, QString> > i(url.queryItems());
|
||||||
|
while (i.hasNext())
|
||||||
|
{
|
||||||
|
QPair<QString, QString> pair = i.next();
|
||||||
|
postMap[pair.first] = pair.second;
|
||||||
|
qDebug() << pair.first << "=" << post(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/httprequestparser.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HTTPREQUESTPARSER_H
|
||||||
|
#define HTTPREQUESTPARSER_H
|
||||||
|
|
||||||
|
#include<QHttpRequestHeader>
|
||||||
|
|
||||||
|
class HttpRequestParser : public QHttpRequestHeader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool headerDone;
|
||||||
|
bool messageDone;
|
||||||
|
bool error;
|
||||||
|
QString data;
|
||||||
|
QString path;
|
||||||
|
QMap<QString, QString> postMap;
|
||||||
|
QMap<QString, QString> getMap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HttpRequestParser();
|
||||||
|
~HttpRequestParser();
|
||||||
|
bool isParsable() const;
|
||||||
|
bool isError() const;
|
||||||
|
QString url() const;
|
||||||
|
QString message() const;
|
||||||
|
QString get(const QString key) const;
|
||||||
|
QString post(const QString key) const;
|
||||||
|
void write(QString str);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
73
src/httpresponsegenerator.cpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "httpresponsegenerator.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
void HttpResponseGenerator::setMessage(const QByteArray message)
|
||||||
|
{
|
||||||
|
HttpResponseGenerator::message = message;
|
||||||
|
setContentLength(message.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpResponseGenerator::setMessage(const QString message)
|
||||||
|
{
|
||||||
|
setMessage(message.QString::toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpResponseGenerator::stripMessage()
|
||||||
|
{
|
||||||
|
message.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray HttpResponseGenerator::toByteArray() const
|
||||||
|
{
|
||||||
|
return QHttpResponseHeader::toString().toUtf8() + message;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpResponseGenerator::setContentTypeByExt(const QString ext)
|
||||||
|
{
|
||||||
|
if(ext == "css")
|
||||||
|
{
|
||||||
|
setContentType("text/css");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(ext == "gif")
|
||||||
|
{
|
||||||
|
setContentType("image/gif");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(ext == "htm" || ext == "html")
|
||||||
|
{
|
||||||
|
setContentType("text/html");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(ext == "js")
|
||||||
|
{
|
||||||
|
setContentType("text/javascript");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(ext == "png")
|
||||||
|
{
|
||||||
|
setContentType("image/x-png");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/httpresponsegenerator.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HTTPRESPONSEGENERATOR_H
|
||||||
|
#define HTTPRESPONSEGENERATOR_H
|
||||||
|
|
||||||
|
#include<QHttpResponseHeader>
|
||||||
|
|
||||||
|
class HttpResponseGenerator : public QHttpResponseHeader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
QByteArray message;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setMessage(const QByteArray message);
|
||||||
|
void setMessage(const QString message);
|
||||||
|
void stripMessage();
|
||||||
|
void setContentTypeByExt(const QString ext);
|
||||||
|
virtual QByteArray toByteArray() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
98
src/httpserver.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "httpserver.h"
|
||||||
|
#include "httpconnection.h"
|
||||||
|
#include "eventmanager.h"
|
||||||
|
#include "bittorrent.h"
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
HttpServer::HttpServer(bittorrent *BTSession, int msec, QObject* parent) : QTcpServer(parent)
|
||||||
|
{
|
||||||
|
base64 = QByteArray(":").toBase64();
|
||||||
|
connect(this, SIGNAL(newConnection()), this, SLOT(newHttpConnection()));
|
||||||
|
HttpServer::BTSession = BTSession;
|
||||||
|
manager = new EventManager(this);
|
||||||
|
//add torrents
|
||||||
|
QStringList list = BTSession->getUnfinishedTorrents() + BTSession->getFinishedTorrents();
|
||||||
|
QString hash;
|
||||||
|
foreach(hash, list)
|
||||||
|
{
|
||||||
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
|
if(h.is_valid())
|
||||||
|
manager->addedTorrent(QString(), h);
|
||||||
|
}
|
||||||
|
//connect BTSession to manager
|
||||||
|
connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), manager, SLOT(addedTorrent(QString, QTorrentHandle&)));
|
||||||
|
connect(BTSession, SIGNAL(deletedTorrent(QString)), manager, SLOT(deletedTorrent(QString)));
|
||||||
|
//set timer
|
||||||
|
QTimer *timer = new QTimer(this);
|
||||||
|
connect(timer, SIGNAL(timeout()), this, SLOT(onTimer()));
|
||||||
|
timer->start(msec);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpServer::~HttpServer()
|
||||||
|
{
|
||||||
|
delete manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::newHttpConnection()
|
||||||
|
{
|
||||||
|
QTcpSocket *socket;
|
||||||
|
while((socket = nextPendingConnection()))
|
||||||
|
{
|
||||||
|
HttpConnection *connection = new HttpConnection(socket, this);
|
||||||
|
//connect connection to BTSession
|
||||||
|
connect(connection, SIGNAL(urlsReadyToBeDownloaded(const QStringList&)), BTSession, SLOT(downloadFromURLList(const QStringList&)));
|
||||||
|
connect(connection, SIGNAL(deleteTorrent(QString)), BTSession, SLOT(deleteTorrent(QString)));
|
||||||
|
connect(connection, SIGNAL(pauseTorrent(QString)), BTSession, SLOT(pauseTorrent(QString)));
|
||||||
|
connect(connection, SIGNAL(resumeTorrent(QString)), BTSession, SLOT(resumeTorrent(QString)));
|
||||||
|
connect(connection, SIGNAL(pauseAllTorrents()), BTSession, SLOT(pauseAllTorrents()));
|
||||||
|
connect(connection, SIGNAL(resumeAllTorrents()), BTSession, SLOT(resumeAllTorrents()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::onTimer()
|
||||||
|
{
|
||||||
|
QStringList list = BTSession->getUnfinishedTorrents() + BTSession->getFinishedTorrents();
|
||||||
|
foreach(QString hash, list)
|
||||||
|
{
|
||||||
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
|
if(h.is_valid())
|
||||||
|
manager->modifiedTorrent(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::setAuthorization(QString username, QString password)
|
||||||
|
{
|
||||||
|
QString cat = username + ":" + password;
|
||||||
|
base64 = QByteArray(cat.toUtf8()).toBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpServer::isAuthorized(QByteArray auth) const
|
||||||
|
{
|
||||||
|
return (auth == base64);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventManager *HttpServer::eventManager() const
|
||||||
|
{
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
53
src/httpserver.h
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef HTTPSERVER_H
|
||||||
|
#define HTTPSERVER_H
|
||||||
|
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
|
class bittorrent;
|
||||||
|
|
||||||
|
class EventManager;
|
||||||
|
|
||||||
|
class HttpServer : public QTcpServer
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
QByteArray base64;
|
||||||
|
bittorrent *BTSession;
|
||||||
|
EventManager *manager;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HttpServer(bittorrent *BTSession, int msec, QObject* parent = 0);
|
||||||
|
~HttpServer();
|
||||||
|
void setAuthorization(QString username, QString password);
|
||||||
|
bool isAuthorized(QByteArray auth) const;
|
||||||
|
EventManager *eventManager() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void newHttpConnection();
|
||||||
|
void onTimer();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
172
src/icons.qrc
@@ -1,93 +1,105 @@
|
|||||||
<!DOCTYPE RCC><RCC version="1.0">
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
<qresource>
|
<qresource>
|
||||||
<file>Icons/time.png</file>
|
|
||||||
<file>Icons/connection.png</file>
|
|
||||||
<file>Icons/locale.png</file>
|
|
||||||
<file>Icons/newmsg.png</file>
|
|
||||||
<file>Icons/url.png</file>
|
|
||||||
<file>Icons/button_ok.png</file>
|
|
||||||
<file>Icons/stare.png</file>
|
|
||||||
<file>Icons/log.png</file>
|
|
||||||
<file>Icons/qbittorrent32.png</file>
|
|
||||||
<file>Icons/downarrow.png</file>
|
|
||||||
<file>Icons/description.png</file>
|
|
||||||
<file>Icons/qbittorrent16.png</file>
|
|
||||||
<file>Icons/exec.png</file>
|
|
||||||
<file>Icons/systemtray.png</file>
|
|
||||||
<file>Icons/unhappy.png</file>
|
|
||||||
<file>Icons/filter.png</file>
|
|
||||||
<file>Icons/sphere.png</file>
|
|
||||||
<file>Icons/mascot.png</file>
|
|
||||||
<file>Icons/encrypted.png</file>
|
|
||||||
<file>Icons/refresh.png</file>
|
|
||||||
<file>Icons/uparrow.png</file>
|
|
||||||
<file>Icons/style.png</file>
|
|
||||||
<file>Icons/wizard.png</file>
|
|
||||||
<file>Icons/password.png</file>
|
|
||||||
<file>Icons/rss.png</file>
|
|
||||||
<file>Icons/sphere2.png</file>
|
|
||||||
<file>Icons/smile.png</file>
|
|
||||||
<file>Icons/loading.png</file>
|
|
||||||
<file>Icons/button_cancel.png</file>
|
|
||||||
<file>Icons/qbittorrent22.png</file>
|
|
||||||
<file>Icons/proxy.png</file>
|
|
||||||
<file>Icons/add_folder.png</file>
|
|
||||||
<file>Icons/unavailable.png</file>
|
|
||||||
<file>Icons/add_file.png</file>
|
<file>Icons/add_file.png</file>
|
||||||
|
<file>Icons/add_folder.png</file>
|
||||||
|
<file>Icons/bt_settings.png</file>
|
||||||
|
<file>Icons/button_cancel.png</file>
|
||||||
|
<file>Icons/button_ok.png</file>
|
||||||
|
<file>Icons/configure.png</file>
|
||||||
|
<file>Icons/connection.png</file>
|
||||||
|
<file>Icons/description.png</file>
|
||||||
|
<file>Icons/downarrow.png</file>
|
||||||
|
<file>Icons/download.png</file>
|
||||||
|
<file>Icons/edit_clear.png</file>
|
||||||
|
<file>Icons/encrypted.png</file>
|
||||||
|
<file>Icons/file.png</file>
|
||||||
|
<file>Icons/filter.png</file>
|
||||||
|
<file>Icons/folder.png</file>
|
||||||
|
<file>Icons/gear.png</file>
|
||||||
<file>Icons/home.png</file>
|
<file>Icons/home.png</file>
|
||||||
|
<file>Icons/loading.png</file>
|
||||||
|
<file>Icons/locale.png</file>
|
||||||
|
<file>Icons/log.png</file>
|
||||||
|
<file>Icons/mascot.png</file>
|
||||||
|
<file>Icons/money.png</file>
|
||||||
|
<file>Icons/newmsg.png</file>
|
||||||
|
<file>Icons/password.png</file>
|
||||||
|
<file>Icons/proxy.png</file>
|
||||||
|
<file>Icons/qbittorrent16.png</file>
|
||||||
|
<file>Icons/qbittorrent22.png</file>
|
||||||
|
<file>Icons/qbittorrent32.png</file>
|
||||||
|
<file>Icons/refresh.png</file>
|
||||||
|
<file>Icons/rss16.png</file>
|
||||||
|
<file>Icons/rss32.png</file>
|
||||||
|
<file>Icons/smile.png</file>
|
||||||
|
<file>Icons/sphere.png</file>
|
||||||
|
<file>Icons/sphere2.png</file>
|
||||||
<file>Icons/splash.png</file>
|
<file>Icons/splash.png</file>
|
||||||
<file>Icons/flags/portugal.png</file>
|
<file>Icons/star.png</file>
|
||||||
|
<file>Icons/stare.png</file>
|
||||||
|
<file>Icons/style.png</file>
|
||||||
|
<file>Icons/subscribe.png</file>
|
||||||
|
<file>Icons/subscribe16.png</file>
|
||||||
|
<file>Icons/systemtray.png</file>
|
||||||
|
<file>Icons/time.png</file>
|
||||||
|
<file>Icons/unavailable.png</file>
|
||||||
|
<file>Icons/unhappy.png</file>
|
||||||
|
<file>Icons/unsubscribe.png</file>
|
||||||
|
<file>Icons/unsubscribe16.png</file>
|
||||||
|
<file>Icons/uparrow.png</file>
|
||||||
|
<file>Icons/url.png</file>
|
||||||
|
<file>Icons/wizard.png</file>
|
||||||
|
<file>Icons/flags/brazil.png</file>
|
||||||
|
<file>Icons/flags/bulgaria.png</file>
|
||||||
|
<file>Icons/flags/china.png</file>
|
||||||
|
<file>Icons/flags/denmark.png</file>
|
||||||
|
<file>Icons/flags/finland.png</file>
|
||||||
<file>Icons/flags/france.png</file>
|
<file>Icons/flags/france.png</file>
|
||||||
|
<file>Icons/flags/germany.png</file>
|
||||||
|
<file>Icons/flags/greece.png</file>
|
||||||
|
<file>Icons/flags/hungary.png</file>
|
||||||
|
<file>Icons/flags/italy.png</file>
|
||||||
|
<file>Icons/flags/japan.png</file>
|
||||||
|
<file>Icons/flags/netherlands.png</file>
|
||||||
|
<file>Icons/flags/norway.png</file>
|
||||||
|
<file>Icons/flags/poland.png</file>
|
||||||
|
<file>Icons/flags/portugal.png</file>
|
||||||
|
<file>Icons/flags/romania.png</file>
|
||||||
|
<file>Icons/flags/russia.png</file>
|
||||||
|
<file>Icons/flags/slovakia.png</file>
|
||||||
|
<file>Icons/flags/south_korea.png</file>
|
||||||
|
<file>Icons/flags/spain.png</file>
|
||||||
|
<file>Icons/flags/spain_catalunya.png</file>
|
||||||
|
<file>Icons/flags/sweden.png</file>
|
||||||
|
<file>Icons/flags/turkey.png</file>
|
||||||
<file>Icons/flags/ukraine.png</file>
|
<file>Icons/flags/ukraine.png</file>
|
||||||
<file>Icons/flags/united_kingdom.png</file>
|
<file>Icons/flags/united_kingdom.png</file>
|
||||||
<file>Icons/flags/germany.png</file>
|
|
||||||
<file>Icons/flags/russia.png</file>
|
|
||||||
<file>Icons/flags/netherlands.png</file>
|
|
||||||
<file>Icons/flags/slovakia.png</file>
|
|
||||||
<file>Icons/flags/spain.png</file>
|
|
||||||
<file>Icons/flags/finland.png</file>
|
|
||||||
<file>Icons/flags/china_hong_kong.png</file>
|
|
||||||
<file>Icons/flags/spain_catalunya.png</file>
|
|
||||||
<file>Icons/flags/poland.png</file>
|
|
||||||
<file>Icons/flags/hungary.png</file>
|
|
||||||
<file>Icons/flags/norway.png</file>
|
|
||||||
<file>Icons/flags/denmark.png</file>
|
|
||||||
<file>Icons/flags/italy.png</file>
|
|
||||||
<file>Icons/flags/china.png</file>
|
|
||||||
<file>Icons/flags/brazil.png</file>
|
|
||||||
<file>Icons/flags/south_korea.png</file>
|
|
||||||
<file>Icons/flags/turkey.png</file>
|
|
||||||
<file>Icons/flags/sweden.png</file>
|
|
||||||
<file>Icons/flags/bulgaria.png</file>
|
|
||||||
<file>Icons/flags/romania.png</file>
|
|
||||||
<file>Icons/flags/japan.png</file>
|
|
||||||
<file>Icons/flags/greece.png</file>
|
|
||||||
<file>Icons/skin/properties.png</file>
|
|
||||||
<file>Icons/skin/play_all.png</file>
|
|
||||||
<file>Icons/skin/remove.png</file>
|
|
||||||
<file>Icons/skin/settings.png</file>
|
|
||||||
<file>Icons/skin/open.png</file>
|
|
||||||
<file>Icons/skin/disconnected.png</file>
|
|
||||||
<file>Icons/skin/delete.png</file>
|
|
||||||
<file>Icons/skin/connected.png</file>
|
|
||||||
<file>Icons/skin/url.png</file>
|
|
||||||
<file>Icons/skin/pause_all.png</file>
|
|
||||||
<file>Icons/skin/downloading.png</file>
|
|
||||||
<file>Icons/skin/play.png</file>
|
|
||||||
<file>Icons/skin/search.png</file>
|
|
||||||
<file>Icons/skin/exit.png</file>
|
|
||||||
<file>Icons/skin/pause.png</file>
|
|
||||||
<file>Icons/skin/firewalled.png</file>
|
|
||||||
<file>Icons/skin/seeding.png</file>
|
|
||||||
<file>Icons/skin/paused.png</file>
|
|
||||||
<file>Icons/skin/preview.png</file>
|
|
||||||
<file>Icons/skin/delete_perm.png</file>
|
|
||||||
<file>Icons/skin/connecting.png</file>
|
|
||||||
<file>Icons/skin/add.png</file>
|
<file>Icons/skin/add.png</file>
|
||||||
<file>Icons/skin/stalled.png</file>
|
<file>Icons/skin/connected.png</file>
|
||||||
<file>Icons/skin/new.png</file>
|
<file>Icons/skin/connecting.png</file>
|
||||||
<file>Icons/skin/qb_question.png</file>
|
<file>Icons/skin/delete.png</file>
|
||||||
<file>Icons/skin/delete_all.png</file>
|
<file>Icons/skin/delete_all.png</file>
|
||||||
|
<file>Icons/skin/delete_perm.png</file>
|
||||||
|
<file>Icons/skin/disconnected.png</file>
|
||||||
|
<file>Icons/skin/downloading.png</file>
|
||||||
|
<file>Icons/skin/exit.png</file>
|
||||||
|
<file>Icons/skin/firewalled.png</file>
|
||||||
<file>Icons/skin/info.png</file>
|
<file>Icons/skin/info.png</file>
|
||||||
|
<file>Icons/skin/new.png</file>
|
||||||
|
<file>Icons/skin/open.png</file>
|
||||||
|
<file>Icons/skin/pause.png</file>
|
||||||
|
<file>Icons/skin/pause_all.png</file>
|
||||||
|
<file>Icons/skin/paused.png</file>
|
||||||
|
<file>Icons/skin/play.png</file>
|
||||||
|
<file>Icons/skin/play_all.png</file>
|
||||||
|
<file>Icons/skin/preview.png</file>
|
||||||
|
<file>Icons/skin/properties.png</file>
|
||||||
|
<file>Icons/skin/qb_question.png</file>
|
||||||
|
<file>Icons/skin/remove.png</file>
|
||||||
|
<file>Icons/skin/search.png</file>
|
||||||
|
<file>Icons/skin/seeding.png</file>
|
||||||
|
<file>Icons/skin/settings.png</file>
|
||||||
|
<file>Icons/skin/stalled.png</file>
|
||||||
|
<file>Icons/skin/url.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
103
src/json.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "json.h"
|
||||||
|
|
||||||
|
QString toJson(QVariant v)
|
||||||
|
{
|
||||||
|
if (v.isNull())
|
||||||
|
return "null";
|
||||||
|
QString result;
|
||||||
|
switch(v.type())
|
||||||
|
{
|
||||||
|
case QVariant::Bool:
|
||||||
|
case QVariant::Double:
|
||||||
|
case QVariant::Int:
|
||||||
|
case QVariant::LongLong:
|
||||||
|
case QVariant::UInt:
|
||||||
|
case QVariant::ULongLong:
|
||||||
|
return v.value<QString>();
|
||||||
|
case QVariant::String:
|
||||||
|
{
|
||||||
|
QString s = v.value<QString>();
|
||||||
|
result = "\"";
|
||||||
|
for(int i=0; i<s.size(); i++)
|
||||||
|
{
|
||||||
|
QChar ch = s[i];
|
||||||
|
switch(ch.toAscii())
|
||||||
|
{
|
||||||
|
case '\b':
|
||||||
|
result += "\\b";
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
result += "\\f";
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
result += "\\n";
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
result += "\\r";
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
result += "\\t";
|
||||||
|
break;
|
||||||
|
case '\"':
|
||||||
|
case '\'':
|
||||||
|
case '\\':
|
||||||
|
case '&':
|
||||||
|
result += '\\';
|
||||||
|
case '\0':
|
||||||
|
default:
|
||||||
|
result += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += "\"";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case QVariant::List:
|
||||||
|
{
|
||||||
|
result = "[";
|
||||||
|
QListIterator<QVariant> it(v.value<QVariantList>());
|
||||||
|
while (it.hasNext())
|
||||||
|
result += toJson(it.next()) + ",";
|
||||||
|
if(result.size() > 1)
|
||||||
|
result.chop(1);
|
||||||
|
result += "]";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case QVariant::Map:
|
||||||
|
{
|
||||||
|
result = "{";
|
||||||
|
QMapIterator<QString, QVariant> it(v.value<QVariantMap>());
|
||||||
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
if(it.value().isValid())
|
||||||
|
result += toJson(QVariant(it.key())) + ":" + toJson(it.value()) + ",";
|
||||||
|
}
|
||||||
|
if(result.size() > 1)
|
||||||
|
result.chop(1);
|
||||||
|
result += "}";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "undefined";
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/json.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2007 by Ishan Arora
|
||||||
|
* ishanarora@gmail.com
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the
|
||||||
|
* Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef JSON_H
|
||||||
|
#define JSON_H
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
QString toJson(QVariant v);
|
||||||
|
|
||||||
|
#endif
|
||||||
36
src/lang.qrc
@@ -1,29 +1,29 @@
|
|||||||
<!DOCTYPE RCC><RCC version="1.0">
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
<qresource>
|
<qresource>
|
||||||
<file>lang/qbittorrent_es.qm</file>
|
<file>lang/qbittorrent_bg.qm</file>
|
||||||
<file>lang/qbittorrent_ko.qm</file>
|
<file>lang/qbittorrent_ca.qm</file>
|
||||||
<file>lang/qbittorrent_hu.qm</file>
|
<file>lang/qbittorrent_da.qm</file>
|
||||||
<file>lang/qbittorrent_sk.qm</file>
|
<file>lang/qbittorrent_de.qm</file>
|
||||||
<file>lang/qbittorrent_uk.qm</file>
|
|
||||||
<file>lang/qbittorrent_ro.qm</file>
|
|
||||||
<file>lang/qbittorrent_ru.qm</file>
|
|
||||||
<file>lang/qbittorrent_nb.qm</file>
|
|
||||||
<file>lang/qbittorrent_el.qm</file>
|
<file>lang/qbittorrent_el.qm</file>
|
||||||
<file>lang/qbittorrent_en.qm</file>
|
<file>lang/qbittorrent_en.qm</file>
|
||||||
<file>lang/qbittorrent_pt_BR.qm</file>
|
<file>lang/qbittorrent_es.qm</file>
|
||||||
|
<file>lang/qbittorrent_fi.qm</file>
|
||||||
<file>lang/qbittorrent_fr.qm</file>
|
<file>lang/qbittorrent_fr.qm</file>
|
||||||
|
<file>lang/qbittorrent_hu.qm</file>
|
||||||
|
<file>lang/qbittorrent_it.qm</file>
|
||||||
|
<file>lang/qbittorrent_ja.qm</file>
|
||||||
|
<file>lang/qbittorrent_ko.qm</file>
|
||||||
|
<file>lang/qbittorrent_nb.qm</file>
|
||||||
<file>lang/qbittorrent_nl.qm</file>
|
<file>lang/qbittorrent_nl.qm</file>
|
||||||
<file>lang/qbittorrent_pl.qm</file>
|
<file>lang/qbittorrent_pl.qm</file>
|
||||||
<file>lang/qbittorrent_it.qm</file>
|
|
||||||
<file>lang/qbittorrent_zh.qm</file>
|
|
||||||
<file>lang/qbittorrent_ca.qm</file>
|
|
||||||
<file>lang/qbittorrent_pt.qm</file>
|
<file>lang/qbittorrent_pt.qm</file>
|
||||||
<file>lang/qbittorrent_da.qm</file>
|
<file>lang/qbittorrent_pt_BR.qm</file>
|
||||||
<file>lang/qbittorrent_tr.qm</file>
|
<file>lang/qbittorrent_ro.qm</file>
|
||||||
<file>lang/qbittorrent_bg.qm</file>
|
<file>lang/qbittorrent_ru.qm</file>
|
||||||
<file>lang/qbittorrent_de.qm</file>
|
<file>lang/qbittorrent_sk.qm</file>
|
||||||
<file>lang/qbittorrent_sv.qm</file>
|
<file>lang/qbittorrent_sv.qm</file>
|
||||||
<file>lang/qbittorrent_ja.qm</file>
|
<file>lang/qbittorrent_tr.qm</file>
|
||||||
<file>lang/qbittorrent_fi.qm</file>
|
<file>lang/qbittorrent_uk.qm</file>
|
||||||
|
<file>lang/qbittorrent_zh.qm</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||