- Moved v1.0.x to a branch so that we work on v1.1.x in trunk

This commit is contained in:
Christophe Dumez 2007-11-06 11:07:07 +00:00
commit 6ae21c2919
156 changed files with 35531 additions and 17986 deletions

View file

@ -1,5 +1,5 @@
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>

View file

@ -5,6 +5,8 @@
- 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 +32,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 +63,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

21
INSTALL
View file

@ -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,11 +14,12 @@ 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.
@ -26,13 +27,17 @@ Dependencies:
- libcommoncpp2 - libcommoncpp2
- 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
View file

@ -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
------------------------------------------ ------------------------------------------

107
TODO
View file

@ -1,98 +1,67 @@
// 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) - Display a progress bar that really represents 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 - File selection in a torrent in compact mode
- Allow to prioritize torrents - Allow to prioritize torrents (may code this in qBittorrent?)
// 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 carefull, the link can be relative
// 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 - Tabs support in search
- Allow to hide columns? - Allow to hide columns?
- Allow to scan multiple directories? (useful?) - Allow to scan multiple directories? (useful?)
- Web interface (turbogears? php?) - Web interface (turbogears? php? python?)
* Webserver? Try to write a webserver as a plugin for qBittorrent in Python
* http://fragments.turtlemeat.com/pythonwebserver.php
- improve and test tracker authentication code (remember login/pass) (need a tracker to test this) - improve and test tracker authentication code (remember login/pass) (need a tracker to test this)
- support zipped torrents? (useful?) - 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) - Allow to limit the number of downloading torrents simultaneously (other are paused until a download finishes)
- Add "Mark all as read" feature for RSS - Improve search plugin install (choose in a list taken from plugins.qbittorrent.org)
- Allow to customize lists refreshing interval (in options) - Display the number of DHT node if possible
- Use search engines as plugins (split them, load them dynamically) to allow the user to add some - When adding a duplicate torrent, check if the trackers are different from the existing one and ask the user if he wants to add them
- Display in torrent addition dialog:
* free disk space on selected drive
* free disk space after torrent download (and/or torrent size)
- Start minimized option in program preferences
- In finished list, replace "Seeds/Leechs" column by "Leechers" because Seeds are always 0.
- Allow to change action on double-click
-> in download list
-> in seeding list
// in v1.0.0 (partial) - WIP // in v1.0.0 - FEATURE FREEZE
- Check storage st creation + hasher in torrent creation
- Fix all (or almost all) opened bugs in bug tracker - Fix all (or almost all) opened bugs in bug tracker
- update sorting when a new torrent is added? - Recheck doc
- Keep documention up to date - Translations update (IN PROGRESS)
- 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: rc6->rc7 changelog:
- French *BETA3* - BUGFIX: Catching DHT exception in case there is a problem
- English *BETA3* - BUGFIX: Removed build dependency on Python
- Japanese *BETA3* - BUGFIX: Fixed a bug in children update when changing files priorities
- Swedish *BETA3* - BUGFIX: Pause/Start All now affect all tabs, not only the current one
- Slovak *BETA3* - BUGFIX: Don't reload all torrents everytime settings are saved
- Ukrainian *BETA3* - BUGFIX: Don't reload seeding torrents anymore (no point)
- Chinese (simplified) *BETA4* - I18N: Updated Turkish translation
- 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

129
configure vendored
View file

@ -27,6 +27,9 @@ Dependency options:
--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
} }
@ -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
@ -218,6 +236,9 @@ echo QC_WITH_LIBCOMMONCPP2_LIB=$QC_WITH_LIBCOMMONCPP2_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")){
@ -512,15 +535,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"))){
@ -593,13 +618,15 @@ 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(!QFile::exists(s+QString("/libMagick++.so"))){
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(QFile::exists(s+QString("libMagick++.so"))){
@ -618,35 +645,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);
@ -661,8 +741,8 @@ cat >$1/modules_new.cpp <<EOT
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
@ -1618,6 +1698,9 @@ export QC_WITH_LIBCOMMONCPP2_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
( (

Binary file not shown.

View file

@ -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'>
@ -16,7 +16,5 @@
<required/> <required/>
</dep> </dep>
<dep type='libmagick'/> <dep type='libmagick'/>
<dep type='python'> <dep type='libzzip'/>
<required/>
</dep>
</qconf> </qconf>

View file

@ -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"))){

View file

@ -44,13 +44,15 @@ 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(!QFile::exists(s+QString("/libMagick++.so"))){
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(QFile::exists(s+QString("libMagick++.so"))){
@ -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;

View file

@ -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")){

71
qcm/libzzip.qcm Normal file
View 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;
}
};

View file

@ -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
View 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);
}
};

View file

@ -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);
}
};

View file

@ -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:

View file

@ -76,11 +76,9 @@ class FinishedListDelegate: 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:

View file

@ -122,7 +122,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);
} }
} }
@ -209,6 +210,10 @@ 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) {
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)h.progress()));
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,10 +223,8 @@ 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())); finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)h.progress()));
continue; continue;
} }
@ -311,7 +314,7 @@ void FinishedTorrents::displayFinishedListMenu(const QPoint& pos){
// 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"); QSettings settings("qBittorrent", "qBittorrent");
QString previewProgram = settings.value("Options/Misc/PreviewProgram", QString()).toString(); QString previewProgram = settings.value("Preferences/general/MediaPlayer", 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) {

View file

@ -47,7 +47,6 @@
#include "allocationDlg.h" #include "allocationDlg.h"
using namespace libtorrent; using namespace libtorrent;
namespace fs = boost::filesystem;
/***************************************************** /*****************************************************
* * * *
@ -56,11 +55,11 @@ namespace fs = boost::filesystem;
*****************************************************/ *****************************************************/
// Constructor // Constructor
GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), force_exit(false) { GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), displaySpeedInTitle(false), force_exit(false), refreshInterval(1500) {
setupUi(this); setupUi(this);
setWindowTitle(tr("qBittorrent %1", "e.g: qBittorrent v0.x").arg(QString::fromUtf8(VERSION))); setWindowTitle(tr("qBittorrent %1", "e.g: qBittorrent v0.x").arg(QString::fromUtf8(VERSION)));
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
systrayIntegration = settings.value(QString::fromUtf8("Options/Misc/Behaviour/SystrayIntegration"), true).toBool(); systrayIntegration = settings.value(QString::fromUtf8("Preferences/General/SystrayEnabled"), true).toBool();
// Create tray icon // Create tray icon
if (QSystemTrayIcon::isSystemTrayAvailable()) { if (QSystemTrayIcon::isSystemTrayAvailable()) {
if(systrayIntegration) { if(systrayIntegration) {
@ -112,6 +111,7 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
connect(BTSession, SIGNAL(scanDirFoundTorrents(const QStringList&)), this, SLOT(processScannedFiles(const QStringList&))); connect(BTSession, SIGNAL(scanDirFoundTorrents(const QStringList&)), this, SLOT(processScannedFiles(const QStringList&)));
connect(BTSession, SIGNAL(newDownloadedTorrent(QString, QString)), this, SLOT(processDownloadedFiles(QString, QString))); connect(BTSession, SIGNAL(newDownloadedTorrent(QString, QString)), this, SLOT(processDownloadedFiles(QString, QString)));
connect(BTSession, SIGNAL(downloadFromUrlFailure(QString, QString)), this, SLOT(handleDownloadFromUrlFailure(QString, QString))); connect(BTSession, SIGNAL(downloadFromUrlFailure(QString, QString)), this, SLOT(handleDownloadFromUrlFailure(QString, QString)));
connect(BTSession, SIGNAL(torrent_deleted(QString, QString, bool)), this, SLOT(deleteTorrent(QString, QString, bool)));
qDebug("create tabWidget"); qDebug("create tabWidget");
tabs = new QTabWidget(); tabs = new QTabWidget();
// Download torrents tab // Download torrents tab
@ -130,6 +130,10 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
// Smooth torrent switching between tabs Downloading <--> Finished // Smooth torrent switching between tabs Downloading <--> Finished
connect(downloadingTorrentTab, SIGNAL(torrentFinished(QString)), finishedTorrentTab, SLOT(addTorrent(QString))); connect(downloadingTorrentTab, SIGNAL(torrentFinished(QString)), finishedTorrentTab, SLOT(addTorrent(QString)));
connect(finishedTorrentTab, SIGNAL(torrentMovedFromFinishedList(QString)), downloadingTorrentTab, SLOT(addTorrent(QString))); connect(finishedTorrentTab, SIGNAL(torrentMovedFromFinishedList(QString)), downloadingTorrentTab, SLOT(addTorrent(QString)));
// Start download list refresher
refresher = new QTimer(this);
connect(refresher, SIGNAL(timeout()), this, SLOT(updateLists()));
refresher->start(1500);
// Configure BT session according to options // Configure BT session according to options
configureSession(true); configureSession(true);
// Resume unfinished torrents // Resume unfinished torrents
@ -141,7 +145,7 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
// RSS tab // RSS tab
rssWidget = new RSSImp(); rssWidget = new RSSImp();
tabs->addTab(rssWidget, tr("RSS")); tabs->addTab(rssWidget, tr("RSS"));
tabs->setTabIcon(3, QIcon(QString::fromUtf8(":/Icons/rss.png"))); tabs->setTabIcon(3, QIcon(QString::fromUtf8(":/Icons/rss32.png")));
readSettings(); readSettings();
// Add torrent given on command line // Add torrent given on command line
processParams(torrentCmdLine); processParams(torrentCmdLine);
@ -155,10 +159,6 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
checkConnect = new QTimer(this); checkConnect = new QTimer(this);
connect(checkConnect, SIGNAL(timeout()), this, SLOT(checkConnectionStatus())); connect(checkConnect, SIGNAL(timeout()), this, SLOT(checkConnectionStatus()));
checkConnect->start(5000); checkConnect->start(5000);
// Start download list refresher
refresher = new QTimer(this);
connect(refresher, SIGNAL(timeout()), this, SLOT(updateLists()));
refresher->start(1500);
previewProcess = new QProcess(this); previewProcess = new QProcess(this);
connect(previewProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(cleanTempPreviewFile(int, QProcess::ExitStatus))); connect(previewProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(cleanTempPreviewFile(int, QProcess::ExitStatus)));
// Accept drag 'n drops // Accept drag 'n drops
@ -243,7 +243,7 @@ void GUI::finishedTorrent(QTorrentHandle& h) const {
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
bool show_msg = true; bool show_msg = true;
QString fileName = h.name(); QString fileName = h.name();
int useOSD = settings.value(QString::fromUtf8("Options/OSDEnabled"), 1).toInt(); bool useNotificationBalloons = settings.value(QString::fromUtf8("Preferences/General/NotificationBaloons"), true).toBool();
// Add it to finished tab // Add it to finished tab
QString hash = h.hash(); QString hash = h.hash();
if(QFile::exists(misc::qBittorrentPath()+QString::fromUtf8("BT_backup")+QDir::separator()+hash+QString::fromUtf8(".finished"))) { if(QFile::exists(misc::qBittorrentPath()+QString::fromUtf8("BT_backup")+QDir::separator()+hash+QString::fromUtf8(".finished"))) {
@ -254,7 +254,7 @@ void GUI::finishedTorrent(QTorrentHandle& h) const {
downloadingTorrentTab->setInfoBar(tr("%1 has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(fileName)); downloadingTorrentTab->setInfoBar(tr("%1 has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(fileName));
downloadingTorrentTab->deleteTorrent(hash); downloadingTorrentTab->deleteTorrent(hash);
finishedTorrentTab->addTorrent(hash); finishedTorrentTab->addTorrent(hash);
if(show_msg && systrayIntegration && (useOSD == 1 || (useOSD == 2 && (isMinimized() || isHidden())))) { if(show_msg && systrayIntegration && useNotificationBalloons) {
myTrayIcon->showMessage(tr("Download finished"), tr("%1 has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(fileName), QSystemTrayIcon::Information, TIME_TRAY_BALLOON); myTrayIcon->showMessage(tr("Download finished"), tr("%1 has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(fileName), QSystemTrayIcon::Information, TIME_TRAY_BALLOON);
} }
} }
@ -262,8 +262,8 @@ void GUI::finishedTorrent(QTorrentHandle& h) const {
// Notification when disk is full // Notification when disk is full
void GUI::fullDiskError(QTorrentHandle& h) const { void GUI::fullDiskError(QTorrentHandle& h) const {
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
int useOSD = settings.value(QString::fromUtf8("Options/OSDEnabled"), 1).toInt(); bool useNotificationBalloons = settings.value(QString::fromUtf8("Preferences/General/NotificationBaloons"), true).toBool();
if(systrayIntegration && (useOSD == 1 || (useOSD == 2 && (isMinimized() || isHidden())))) { if(systrayIntegration && useNotificationBalloons) {
myTrayIcon->showMessage(tr("I/O Error", "i.e: Input/Output Error"), tr("An error occured when trying to read or write %1. The disk is probably full, download has been paused", "e.g: An error occured when trying to read or write xxx.avi. The disk is probably full, download has been paused").arg(h.name()), QSystemTrayIcon::Critical, TIME_TRAY_BALLOON); myTrayIcon->showMessage(tr("I/O Error", "i.e: Input/Output Error"), tr("An error occured when trying to read or write %1. The disk is probably full, download has been paused", "e.g: An error occured when trying to read or write xxx.avi. The disk is probably full, download has been paused").arg(h.name()), QSystemTrayIcon::Critical, TIME_TRAY_BALLOON);
} }
// Download will be paused by libtorrent. Updating GUI information accordingly // Download will be paused by libtorrent. Updating GUI information accordingly
@ -412,7 +412,7 @@ void GUI::previewFile(QString filePath) {
QStringList params; QStringList params;
params << tmpPath; params << tmpPath;
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
QString previewProgram = settings.value(QString::fromUtf8("Options/Misc/PreviewProgram"), QString()).toString(); QString previewProgram = settings.value(QString::fromUtf8("Preferences/General/MediaPlayer"), QString()).toString();
previewProcess->start(previewProgram, params, QIODevice::ReadOnly); previewProcess->start(previewProgram, params, QIODevice::ReadOnly);
}else{ }else{
QMessageBox::critical(0, tr("Preview process already running"), tr("There is already another preview process running.\nPlease close the other one first.")); QMessageBox::critical(0, tr("Preview process already running"), tr("There is already another preview process running.\nPlease close the other one first."));
@ -475,13 +475,13 @@ void GUI::on_actionAbout_triggered() {
void GUI::closeEvent(QCloseEvent *e) { void GUI::closeEvent(QCloseEvent *e) {
qDebug("Mainwindow received closeEvent"); qDebug("Mainwindow received closeEvent");
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
bool goToSystrayOnExit = settings.value(QString::fromUtf8("Options/Misc/Behaviour/GoToSystrayOnExit"), false).toBool(); bool goToSystrayOnExit = settings.value(QString::fromUtf8("Preferences/General/CloseToTray"), false).toBool();
if(!force_exit && systrayIntegration && goToSystrayOnExit && !this->isHidden()) { if(!force_exit && systrayIntegration && goToSystrayOnExit && !this->isHidden()) {
hide(); hide();
e->ignore(); e->ignore();
return; return;
} }
if(settings.value(QString::fromUtf8("Options/Misc/Behaviour/ConfirmOnExit"), true).toBool() && downloadingTorrentTab->getNbTorrentsInList()) { if(settings.value(QString::fromUtf8("Preferences/General/ExitConfirm"), true).toBool() && downloadingTorrentTab->getNbTorrentsInList()) {
show(); show();
if(!isMaximized()) if(!isMaximized())
showNormal(); showNormal();
@ -511,13 +511,14 @@ void GUI::closeEvent(QCloseEvent *e) {
// Display window to create a torrent // Display window to create a torrent
void GUI::on_actionCreate_torrent_triggered() { void GUI::on_actionCreate_torrent_triggered() {
new createtorrent(this); createtorrent *ct = new createtorrent(this);
connect(ct, SIGNAL(torrent_to_seed(QString)), this, SLOT(addTorrent(QString)));
} }
// Called when we minimize the program // Called when we minimize the program
void GUI::hideEvent(QHideEvent *e) { void GUI::hideEvent(QHideEvent *e) {
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
if(systrayIntegration && settings.value(QString::fromUtf8("Options/Misc/Behaviour/GoToSystray"), true).toBool() && !e->spontaneous()) { if(systrayIntegration && settings.value(QString::fromUtf8("Preferences/General/MinimizeToTray"), false).toBool()) {
// Hide window // Hide window
hide(); hide();
} }
@ -528,13 +529,25 @@ void GUI::hideEvent(QHideEvent *e) {
// Action executed when a file is dropped // Action executed when a file is dropped
void GUI::dropEvent(QDropEvent *event) { void GUI::dropEvent(QDropEvent *event) {
event->acceptProposedAction(); event->acceptProposedAction();
QStringList files=event->mimeData()->text().split(QString::fromUtf8("\n")); QStringList files;
if(event->mimeData()->hasUrls()) {
QList<QUrl> urls = event->mimeData()->urls();
QUrl url;
foreach(url, urls) {
QString tmp = url.toString();
tmp.trimmed();
if(!tmp.isEmpty())
files << url.toString();
}
} else {
files = event->mimeData()->text().split(QString::fromUtf8("\n"));
}
// Add file to download list // Add file to download list
QString file; QString file;
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool(); bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
foreach(file, files) { foreach(file, files) {
file = file.trimmed().replace(QString::fromUtf8("file://"), QString::fromUtf8("")); file = file.trimmed().replace(QString::fromUtf8("file://"), QString::fromUtf8(""), Qt::CaseInsensitive);
qDebug("Dropped file %s on download list", file.toUtf8().data()); qDebug("Dropped file %s on download list", file.toUtf8().data());
if(file.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || file.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || file.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) { if(file.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || file.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || file.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) {
BTSession->downloadFromUrl(file); BTSession->downloadFromUrl(file);
@ -543,7 +556,7 @@ void GUI::dropEvent(QDropEvent *event) {
if(useTorrentAdditionDialog) { if(useTorrentAdditionDialog) {
torrentAdditionDialog *dialog = new torrentAdditionDialog(this); torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString))); connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString)));
connect(dialog, SIGNAL(setInfoBarGUI(QString, QString)), downloadingTorrentTab, SLOT(setInfoBar(QString, QString))); connect(dialog, SIGNAL(setInfoBarGUI(QString, QColor)), downloadingTorrentTab, SLOT(setInfoBar(QString, QColor)));
dialog->showLoad(file); dialog->showLoad(file);
}else{ }else{
BTSession->addTorrent(file); BTSession->addTorrent(file);
@ -553,7 +566,11 @@ void GUI::dropEvent(QDropEvent *event) {
// Decode if we accept drag 'n drop or not // Decode if we accept drag 'n drop or not
void GUI::dragEnterEvent(QDragEnterEvent *event) { void GUI::dragEnterEvent(QDragEnterEvent *event) {
if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain"))) { 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(); event->acceptProposedAction();
} }
} }
@ -567,22 +584,20 @@ void GUI::dragEnterEvent(QDragEnterEvent *event) {
// Display a dialog to allow user to add // Display a dialog to allow user to add
// torrents to download list // torrents to download list
void GUI::on_actionOpen_triggered() { void GUI::on_actionOpen_triggered() {
QStringList pathsList;
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
// Open File Open Dialog // Open File Open Dialog
// Note: it is possible to select more than one file // Note: it is possible to select more than one file
pathsList = QFileDialog::getOpenFileNames(0, QStringList pathsList = QFileDialog::getOpenFileNames(0,
tr("Open Torrent Files"), settings.value(QString::fromUtf8("MainWindowLastDir"), QDir::homePath()).toString(), tr("Open Torrent Files"), settings.value(QString::fromUtf8("MainWindowLastDir"), QDir::homePath()).toString(),
tr("Torrent Files")+QString::fromUtf8(" (*.torrent)")); tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
if(!pathsList.empty()) { if(!pathsList.empty()) {
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool();
unsigned int listSize = pathsList.size(); unsigned int listSize = pathsList.size();
for(unsigned int i=0; i<listSize; ++i) { for(unsigned int i=0; i<listSize; ++i) {
if(useTorrentAdditionDialog) { if(useTorrentAdditionDialog) {
torrentAdditionDialog *dialog = new torrentAdditionDialog(this); torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString))); connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString)));
connect(dialog, SIGNAL(setInfoBarGUI(QString, QString)), downloadingTorrentTab, SLOT(setInfoBar(QString, QString))); connect(dialog, SIGNAL(setInfoBarGUI(QString, QColor)), downloadingTorrentTab, SLOT(setInfoBar(QString, QColor)));
dialog->showLoad(pathsList.at(i)); dialog->showLoad(pathsList.at(i));
}else{ }else{
BTSession->addTorrent(pathsList.at(i)); BTSession->addTorrent(pathsList.at(i));
@ -646,6 +661,16 @@ void GUI::on_actionDelete_Permanently_triggered() {
} }
} }
void GUI::deleteTorrent(QString hash, QString fileName, bool finished) {
if(finished) {
finishedTorrentTab->deleteTorrent(hash);
} else {
downloadingTorrentTab->deleteTorrent(hash);
}
// Update info bar
downloadingTorrentTab->setInfoBar(tr("'%1' was removed because its ratio reached the maximum value you set.", "%1 is a file name").arg(fileName));
}
// delete selected items in the list // delete selected items in the list
void GUI::on_actionDelete_triggered() { void GUI::on_actionDelete_triggered() {
QStringList hashes; QStringList hashes;
@ -708,7 +733,7 @@ void GUI::on_actionDelete_triggered() {
void GUI::processParams(const QStringList& params) { void GUI::processParams(const QStringList& params) {
QString param; QString param;
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool(); bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
foreach(param, params) { foreach(param, params) {
param = param.trimmed(); param = param.trimmed();
if(param.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) { if(param.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) {
@ -717,7 +742,7 @@ void GUI::processParams(const QStringList& params) {
if(useTorrentAdditionDialog) { if(useTorrentAdditionDialog) {
torrentAdditionDialog *dialog = new torrentAdditionDialog(this); torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString))); connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString)));
connect(dialog, SIGNAL(setInfoBarGUI(QString, QString)), downloadingTorrentTab, SLOT(setInfoBar(QString, QString))); connect(dialog, SIGNAL(setInfoBarGUI(QString, QColor)), downloadingTorrentTab, SLOT(setInfoBar(QString, QColor)));
dialog->showLoad(param); dialog->showLoad(param);
}else{ }else{
BTSession->addTorrent(param); BTSession->addTorrent(param);
@ -726,15 +751,19 @@ void GUI::processParams(const QStringList& params) {
} }
} }
void GUI::addTorrent(QString path) {
BTSession->addTorrent(path);
}
void GUI::processScannedFiles(const QStringList& params) { void GUI::processScannedFiles(const QStringList& params) {
QString param; QString param;
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool(); bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
foreach(param, params) { foreach(param, params) {
if(useTorrentAdditionDialog) { if(useTorrentAdditionDialog) {
torrentAdditionDialog *dialog = new torrentAdditionDialog(this); torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString))); connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString)));
connect(dialog, SIGNAL(setInfoBarGUI(QString, QString)), downloadingTorrentTab, SLOT(setInfoBar(QString, QString))); connect(dialog, SIGNAL(setInfoBarGUI(QString, QColor)), downloadingTorrentTab, SLOT(setInfoBar(QString, QColor)));
dialog->showLoad(param, true); dialog->showLoad(param, true);
}else{ }else{
BTSession->addTorrent(param, true); BTSession->addTorrent(param, true);
@ -744,11 +773,11 @@ void GUI::processScannedFiles(const QStringList& params) {
void GUI::processDownloadedFiles(QString path, QString url) { void GUI::processDownloadedFiles(QString path, QString url) {
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Options/Misc/TorrentAdditionDialog/Enabled"), true).toBool(); bool useTorrentAdditionDialog = settings.value(QString::fromUtf8("Preferences/Downloads/AdditionDialog"), true).toBool();
if(useTorrentAdditionDialog) { if(useTorrentAdditionDialog) {
torrentAdditionDialog *dialog = new torrentAdditionDialog(this); torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString))); connect(dialog, SIGNAL(torrentAddition(QString, bool, QString)), BTSession, SLOT(addTorrent(QString, bool, QString)));
connect(dialog, SIGNAL(setInfoBarGUI(QString, QString)), downloadingTorrentTab, SLOT(setInfoBar(QString, QString))); connect(dialog, SIGNAL(setInfoBarGUI(QString, QColor)), downloadingTorrentTab, SLOT(setInfoBar(QString, QColor)));
dialog->showLoad(path, false, url); dialog->showLoad(path, false, url);
}else{ }else{
BTSession->addTorrent(path, false, url); BTSession->addTorrent(path, false, url);
@ -758,92 +787,67 @@ void GUI::processDownloadedFiles(QString path, QString url) {
// Set BT session configuration // Set BT session configuration
void GUI::configureSession(bool deleteOptions) { void GUI::configureSession(bool deleteOptions) {
qDebug("Configuring session"); qDebug("Configuring session");
QPair<int, int> limits; // General
unsigned short old_listenPort, new_listenPort; displaySpeedInTitle = options->speedInTitleBar();
proxy_settings proxySettings; unsigned int new_refreshInterval = options->getRefreshInterval();
session_settings sessionSettings; if(refreshInterval != new_refreshInterval) {
pe_settings encryptionSettings; refreshInterval = new_refreshInterval;
// Configure session regarding options refresher->start(refreshInterval);
}
// Downloads
// * Save path
BTSession->setDefaultSavePath(options->getSavePath()); BTSession->setDefaultSavePath(options->getSavePath());
old_listenPort = BTSession->getListenPort(); BTSession->preAllocateAllFiles(options->preAllocateAllFiles());
BTSession->startTorrentsInPause(options->addTorrentsInPause());
// * Scan dir
if(options->getScanDir().isNull()) {
BTSession->disableDirectoryScanning();
}else{
BTSession->enableDirectoryScanning(options->getScanDir());
}
// Connection
// * Ports binding
unsigned short old_listenPort = BTSession->getListenPort();
BTSession->setListeningPortsRange(options->getPorts()); BTSession->setListeningPortsRange(options->getPorts());
new_listenPort = BTSession->getListenPort(); unsigned short new_listenPort = BTSession->getListenPort();
if(new_listenPort != old_listenPort) { if(new_listenPort != old_listenPort) {
downloadingTorrentTab->setInfoBar(tr("qBittorrent is bind to port: %1", "e.g: qBittorrent is bind to port: 1666").arg( misc::toQString(new_listenPort))); downloadingTorrentTab->setInfoBar(tr("qBittorrent is bind to port: %1", "e.g: qBittorrent is bind to port: 1666").arg( misc::toQString(new_listenPort)));
} }
// Apply max connec limit (-1 if disabled) // * Global download limit
BTSession->setMaxConnections(options->getMaxConnec()); QPair<int, int> limits = options->getGlobalBandwidthLimits();
limits = options->getLimits(); if(limits.first <= 0) {
switch(limits.first) { // Download limit disabled
case -1: // Download limit disabled
case 0:
BTSession->setDownloadRateLimit(-1); BTSession->setDownloadRateLimit(-1);
break; } else {
default: // Enabled
BTSession->setDownloadRateLimit(limits.first*1024); BTSession->setDownloadRateLimit(limits.first*1024);
} }
switch(limits.second) { // * Global Upload limit
case -1: // Upload limit disabled if(limits.second <= 0) {
case 0: // Upload limit disabled
BTSession->setUploadRateLimit(-1); BTSession->setUploadRateLimit(-1);
break; } else {
default: // Enabled
BTSession->setUploadRateLimit(limits.second*1024); BTSession->setUploadRateLimit(limits.second*1024);
} }
// Apply ratio (0 if disabled) // * UPnP
BTSession->setGlobalRatio(options->getRatio()); if(options->isUPnPEnabled()) {
// DHT (Trackerless) BTSession->enableUPnP(true);
if(options->isDHTEnabled()) {
downloadingTorrentTab->setInfoBar(tr("DHT support [ON], port: %1").arg(options->getDHTPort()), QString::fromUtf8("blue"));
BTSession->enableDHT();
// Set DHT Port
BTSession->setDHTPort(options->getDHTPort());
}else{
downloadingTorrentTab->setInfoBar(tr("DHT support [OFF]"), QString::fromUtf8("blue"));
BTSession->disableDHT();
}
// UPnP can't be disabled
downloadingTorrentTab->setInfoBar(tr("UPnP support [ON]"), QString::fromUtf8("blue")); downloadingTorrentTab->setInfoBar(tr("UPnP support [ON]"), QString::fromUtf8("blue"));
// Encryption settings } else {
int encryptionState = options->getEncryptionSetting(); BTSession->enableUPnP(false);
// The most secure, rc4 only so that all streams and encrypted downloadingTorrentTab->setInfoBar(tr("UPnP support [OFF]"), QString::fromUtf8("blue"));
encryptionSettings.allowed_enc_level = pe_settings::rc4;
encryptionSettings.prefer_rc4 = true;
switch(encryptionState) {
case 0: //Enabled
encryptionSettings.out_enc_policy = pe_settings::enabled;
encryptionSettings.in_enc_policy = pe_settings::enabled;
downloadingTorrentTab->setInfoBar(tr("Encryption support [ON]"), QString::fromUtf8("blue"));
break;
case 1: // Forced
encryptionSettings.out_enc_policy = pe_settings::forced;
encryptionSettings.in_enc_policy = pe_settings::forced;
downloadingTorrentTab->setInfoBar(tr("Encryption support [FORCED]"), QString::fromUtf8("blue"));
break;
default: // Disabled
encryptionSettings.out_enc_policy = pe_settings::disabled;
encryptionSettings.in_enc_policy = pe_settings::disabled;
downloadingTorrentTab->setInfoBar(tr("Encryption support [OFF]"), QString::fromUtf8("blue"));
} }
BTSession->applyEncryptionSettings(encryptionSettings); // * NAT-PMP
// PeX if(options->isNATPMPEnabled()) {
if(!options->isPeXDisabled()) { BTSession->enableNATPMP(true);
qDebug("Enabling Peer eXchange (PeX)"); downloadingTorrentTab->setInfoBar(tr("NAT-PMP support [ON]"), QString::fromUtf8("blue"));
downloadingTorrentTab->setInfoBar(tr("PeX support [ON]"), QString::fromUtf8("blue")); } else {
BTSession->enablePeerExchange(); BTSession->enableNATPMP(false);
}else{ downloadingTorrentTab->setInfoBar(tr("NAT-PMP support [OFF]"), QString::fromUtf8("blue"));
downloadingTorrentTab->setInfoBar(tr("PeX support [OFF]"), QString::fromUtf8("blue"));
qDebug("Peer eXchange (PeX) disabled");
} }
// Apply filtering settings // * Proxy settings
if(options->isFilteringEnabled()) { proxy_settings proxySettings;
BTSession->enableIPFilter(options->getFilter());
downloadingTorrentTab->setBottomTabEnabled(1, true);
}else{
BTSession->disableIPFilter();
downloadingTorrentTab->setBottomTabEnabled(1, false);
}
// Apply Proxy settings
if(options->isProxyEnabled()) { if(options->isProxyEnabled()) {
switch(options->getProxyType()) { switch(options->getProxyType()) {
case HTTP: case HTTP:
@ -867,14 +871,82 @@ void GUI::configureSession(bool deleteOptions) {
} }
} }
BTSession->setProxySettings(proxySettings, options->useProxyForTrackers(), options->useProxyForPeers(), options->useProxyForWebseeds(), options->useProxyForDHT()); BTSession->setProxySettings(proxySettings, options->useProxyForTrackers(), options->useProxyForPeers(), options->useProxyForWebseeds(), options->useProxyForDHT());
// * Session settings
session_settings sessionSettings;
sessionSettings.user_agent = "qBittorrent "VERSION; sessionSettings.user_agent = "qBittorrent "VERSION;
BTSession->setSessionSettings(sessionSettings); BTSession->setSessionSettings(sessionSettings);
// Scan dir stuff // Bittorrent
if(options->getScanDir().isNull()) { // * Max connections limit
BTSession->disableDirectoryScanning(); BTSession->setMaxConnections(options->getMaxConnecs());
}else{ // * Max connections per torrent limit
BTSession->enableDirectoryScanning(options->getScanDir()); BTSession->setMaxConnectionsPerTorrent(options->getMaxConnecsPerTorrent());
// * Max uploads per torrent limit
BTSession->setMaxUploadsPerTorrent(options->getMaxUploadsPerTorrent());
// * DHT
if(options->isDHTEnabled()) {
// Set DHT Port
BTSession->setDHTPort(new_listenPort);
if(BTSession->enableDHT(true)) {
downloadingTorrentTab->setInfoBar(tr("DHT support [ON], port: %1").arg(new_listenPort), QString::fromUtf8("blue"));
} else {
downloadingTorrentTab->setInfoBar(tr("DHT support [OFF]"), QString::fromUtf8("red"));
} }
} else {
BTSession->enableDHT(false);
downloadingTorrentTab->setInfoBar(tr("DHT support [OFF]"), QString::fromUtf8("blue"));
}
// * PeX
if(options->isPeXEnabled()) {
downloadingTorrentTab->setInfoBar(tr("PeX support [ON]"), QString::fromUtf8("blue"));
BTSession->enablePeerExchange();
}else{
// TODO: How can we remove the extension?
downloadingTorrentTab->setInfoBar(tr("PeX support [OFF]"), QString::fromUtf8("blue"));
}
// * LSD
if(options->isLSDEnabled()) {
BTSession->enableLSD(true);
downloadingTorrentTab->setInfoBar(tr("Local Peer Discovery [ON]"), QString::fromUtf8("blue"));
} else {
BTSession->enableLSD(false);
downloadingTorrentTab->setInfoBar(tr("Local Peer Discovery support [OFF]"), QString::fromUtf8("blue"));
}
// * Encryption
int encryptionState = options->getEncryptionSetting();
// The most secure, rc4 only so that all streams and encrypted
pe_settings encryptionSettings;
encryptionSettings.allowed_enc_level = pe_settings::rc4;
encryptionSettings.prefer_rc4 = true;
switch(encryptionState) {
case 0: //Enabled
encryptionSettings.out_enc_policy = pe_settings::enabled;
encryptionSettings.in_enc_policy = pe_settings::enabled;
downloadingTorrentTab->setInfoBar(tr("Encryption support [ON]"), QString::fromUtf8("blue"));
break;
case 1: // Forced
encryptionSettings.out_enc_policy = pe_settings::forced;
encryptionSettings.in_enc_policy = pe_settings::forced;
downloadingTorrentTab->setInfoBar(tr("Encryption support [FORCED]"), QString::fromUtf8("blue"));
break;
default: // Disabled
encryptionSettings.out_enc_policy = pe_settings::disabled;
encryptionSettings.in_enc_policy = pe_settings::disabled;
downloadingTorrentTab->setInfoBar(tr("Encryption support [OFF]"), QString::fromUtf8("blue"));
}
BTSession->applyEncryptionSettings(encryptionSettings);
// * Desired ratio
BTSession->setGlobalRatio(options->getDesiredRatio());
// * Maximum ratio
BTSession->setDeleteRatio(options->getDeleteRatio());
// Ip Filter
if(options->isFilteringEnabled()) {
BTSession->enableIPFilter(options->getFilter());
downloadingTorrentTab->setBottomTabEnabled(1, true);
}else{
BTSession->disableIPFilter();
downloadingTorrentTab->setBottomTabEnabled(1, false);
}
// Clean up
if(deleteOptions) { if(deleteOptions) {
delete options; delete options;
} }
@ -918,23 +990,18 @@ void GUI::togglePausedState(QString hash) {
// Pause All Downloads in DL list // Pause All Downloads in DL list
void GUI::on_actionPause_All_triggered() { void GUI::on_actionPause_All_triggered() {
bool change = false; bool change = false;
bool inDownloadList = true; QStringList DL_hashes = BTSession->getUnfinishedTorrents();
if(tabs->currentIndex() > 1) return; QStringList F_hashes = BTSession->getFinishedTorrents();
if(tabs->currentIndex() == 1)
inDownloadList = false;
QStringList hashes;
if(inDownloadList) {
hashes = BTSession->getUnfinishedTorrents();
} else {
hashes = BTSession->getFinishedTorrents();
}
QString hash; QString hash;
foreach(hash, hashes) { foreach(hash, DL_hashes) {
if(BTSession->pauseTorrent(hash)){ if(BTSession->pauseTorrent(hash)){
change = true; change = true;
if(inDownloadList)
downloadingTorrentTab->pauseTorrent(hash); downloadingTorrentTab->pauseTorrent(hash);
else }
}
foreach(hash, F_hashes) {
if(BTSession->pauseTorrent(hash)){
change = true;
finishedTorrentTab->pauseTorrent(hash); finishedTorrentTab->pauseTorrent(hash);
} }
} }
@ -970,23 +1037,18 @@ void GUI::on_actionPause_triggered() {
// Resume All Downloads in DL list // Resume All Downloads in DL list
void GUI::on_actionStart_All_triggered() { void GUI::on_actionStart_All_triggered() {
bool change = false; bool change = false;
bool inDownloadList = true; QStringList DL_hashes = BTSession->getUnfinishedTorrents();
if(tabs->currentIndex() > 1) return; QStringList F_hashes = BTSession->getFinishedTorrents();
if(tabs->currentIndex() == 1)
inDownloadList = false;
QStringList hashes;
if(inDownloadList) {
hashes = BTSession->getUnfinishedTorrents();
} else {
hashes = BTSession->getFinishedTorrents();
}
QString hash; QString hash;
foreach(hash, hashes) { foreach(hash, DL_hashes) {
if(BTSession->resumeTorrent(hash)){ if(BTSession->resumeTorrent(hash)){
change = true; change = true;
if(inDownloadList)
downloadingTorrentTab->resumeTorrent(hash); downloadingTorrentTab->resumeTorrent(hash);
else }
}
foreach(hash, F_hashes) {
if(BTSession->resumeTorrent(hash)){
change = true;
finishedTorrentTab->resumeTorrent(hash); finishedTorrentTab->resumeTorrent(hash);
} }
} }
@ -1049,6 +1111,11 @@ void GUI::updateLists() {
default: default:
return; return;
} }
if(displaySpeedInTitle) {
QString dl_rate = QByteArray::number(BTSession->getSessionStatus().payload_download_rate/1024, 'f', 1);
QString up_rate = QByteArray::number(BTSession->getSessionStatus().payload_upload_rate/1024, 'f', 1);
setWindowTitle(tr("qBittorrent %1 (DL: %2KiB/s, UP: %3KiB/s)", "%1 is qBittorrent version").arg(QString::fromUtf8(VERSION)).arg(dl_rate).arg(up_rate));
}
} }
// Called when a tracker requires authentication // Called when a tracker requires authentication
@ -1067,7 +1134,16 @@ void GUI::checkConnectionStatus() {
downloadingTorrentTab->updateRatio(); downloadingTorrentTab->updateRatio();
// update global informations // update global informations
if(systrayIntegration) { if(systrayIntegration) {
myTrayIcon->setToolTip(QString::fromUtf8("<b>")+tr("qBittorrent")+QString::fromUtf8("</b><br>")+tr("DL speed: %1 KiB/s", "e.g: Download speed: 10 KiB/s").arg(QString(QByteArray::number(BTSession->getPayloadDownloadRate()/1024., 'f', 1)))+QString::fromUtf8("<br>")+tr("UP speed: %1 KiB/s", "e.g: Upload speed: 10 KiB/s").arg(QString(QByteArray::number(BTSession->getPayloadUploadRate()/1024., 'f', 1)))); // tray icon QString html = "<div style='background-color: #678db2; color: #fff;height: 18px; font-weight: bold; margin-bottom: 5px;'>";
html += tr("qBittorrent");
html += "</div>";
html += "<div style='vertical-align: baseline; height: 18px;'>";
html += "<img src=':/Icons/skin/downloading.png'/>&nbsp;"+tr("DL speed: %1 KiB/s", "e.g: Download speed: 10 KiB/s").arg(QString(QByteArray::number(BTSession->getPayloadDownloadRate()/1024., 'f', 1)));
html += "</div>";
html += "<div style='vertical-align: baseline; height: 18px;'>";
html += "<img src=':/Icons/skin/seeding.png'/>&nbsp;"+tr("UP speed: %1 KiB/s", "e.g: Upload speed: 10 KiB/s").arg(QString(QByteArray::number(BTSession->getPayloadUploadRate()/1024., 'f', 1)));
html += "</div>";
myTrayIcon->setToolTip(html); // tray icon
} }
session_status sessionStatus = BTSession->getSessionStatus(); session_status sessionStatus = BTSession->getSessionStatus();
if(sessionStatus.has_incoming_connections) { if(sessionStatus.has_incoming_connections) {
@ -1134,7 +1210,7 @@ void GUI::on_actionOptions_triggered() {
// Is executed each time options are saved // Is executed each time options are saved
void GUI::OptionsSaved(QString info, bool deleteOptions) { void GUI::OptionsSaved(QString info, bool deleteOptions) {
bool newSystrayIntegration = options->useSystrayIntegration(); bool newSystrayIntegration = options->systrayIntegration();
if(newSystrayIntegration && !systrayIntegration) { if(newSystrayIntegration && !systrayIntegration) {
// create the trayicon // create the trayicon
createTrayIcon(); createTrayIcon();

View file

@ -64,7 +64,9 @@ class GUI : public QMainWindow, private Ui::MainWindow{
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;
@ -127,10 +129,12 @@ 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, QString fileName, bool finished);
void finishedTorrent(QTorrentHandle& h) const; void finishedTorrent(QTorrentHandle& h) const;
void torrentChecked(QString hash) const; void torrentChecked(QString hash) const;
void updateLists(); void updateLists();

BIN
src/Icons/bt_settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/Icons/configure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 820 B

Before After
Before After

BIN
src/Icons/download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/Icons/edit_clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/Icons/file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 998 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

BIN
src/Icons/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

BIN
src/Icons/gear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 532 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Before After
Before After

View file

@ -4,19 +4,20 @@ Comment=V1.0.0
Encoding=UTF-8 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[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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 948 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

BIN
src/Icons/rss16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

BIN
src/Icons/rss32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before After
Before After

BIN
src/Icons/star.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/Icons/subscribe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/Icons/subscribe16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 856 B

Before After
Before After

BIN
src/Icons/unsubscribe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/Icons/unsubscribe16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

View file

@ -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:

View file

@ -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,30 @@ 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) // }
*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 +198,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 +208,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

View file

@ -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

View file

@ -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" >

View file

@ -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
View 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 file {
private:
file *parent;
bool is_dir;
QString rel_path;
QList<file*> children;
size_type size;
float progress;
int priority;
int index; // Index in torrent_info
public:
file(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);
}
}
~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.;
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);
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<file*> getChildren() const {
return children;
}
file* getChild(QString fileName) const {
Q_ASSERT(is_dir);
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);
}
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);
file *f = new 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;
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:
file *root;
public:
arborescence(torrent_info t) {
torrent_info::file_iterator fi = t.begin_files();
if(t.num_files() > 1) {
root = new file(0, misc::toQString(t.name()), true);
} else {
// XXX: Will crash if there is no file in torrent
root = new 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 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 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;
}
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;
file *dad = root;
unsigned int nb_i = 0;
unsigned int size = fileNames.size();
foreach(fileName, fileNames) {
++nb_i;
if(fileName == ".") continue;
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

View file

@ -24,37 +24,32 @@
#include <QString> #include <QString>
#include <QTimer> #include <QTimer>
#include "bittorrent.h"
#include "misc.h"
#include "downloadThread.h"
#include "deleteThread.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>
#include <libtorrent/entry.hpp> #include <libtorrent/entry.hpp>
#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 ETAS_MAX_VALUES 3
#define ETA_REFRESH_INTERVAL 10000 #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) {
// 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
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();
@ -85,6 +80,42 @@ bittorrent::~bittorrent() {
delete s; delete s;
} }
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;
}
pauseAndReloadTorrent(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_deleted(hash, fileName, true);
}
}
}
void bittorrent::setDownloadLimit(QString hash, long val) { void bittorrent::setDownloadLimit(QString hash, long val) {
QTorrentHandle h = getTorrentHandle(hash); QTorrentHandle h = getTorrentHandle(hash);
if(h.is_valid()) if(h.is_valid())
@ -104,6 +135,10 @@ void bittorrent::handleDownloadFailure(QString url, QString reason) {
emit downloadFromUrlFailure(url, reason); emit downloadFromUrlFailure(url, reason);
} }
void bittorrent::startTorrentsInPause(bool b) {
addInPause = b;
}
void bittorrent::updateETAs() { void bittorrent::updateETAs() {
QString hash; QString hash;
foreach(hash, unfinishedTorrents) { foreach(hash, unfinishedTorrents) {
@ -112,24 +147,37 @@ void bittorrent::updateETAs() {
if(h.is_paused()) continue; if(h.is_paused()) continue;
QString hash = h.hash(); QString hash = h.hash();
QList<qlonglong> listEtas = ETAstats.value(hash, QList<qlonglong>()); QList<qlonglong> listEtas = ETAstats.value(hash, QList<qlonglong>());
// XXX: We can still get an overflow if remaining file size is approximately
// 8.38*10^5 TiB (let's assume this can't happen)
if(h.download_payload_rate() > 0.1) {
if(listEtas.size() == ETAS_MAX_VALUES) { if(listEtas.size() == ETAS_MAX_VALUES) {
listEtas.removeFirst(); listEtas.removeFirst();
} }
if(h.download_payload_rate()) { listEtas << (qlonglong)((h.actual_size()-h.total_wanted_done())/(double)h.download_payload_rate());
listEtas << (qlonglong)((h.actual_size()-h.total_done())/(double)h.download_payload_rate());
ETAstats[hash] = listEtas; ETAstats[hash] = listEtas;
long moy = 0; qlonglong moy = 0;
long val; qlonglong val;
unsigned int nbETAs = listEtas.size(); unsigned int nbETAs = listEtas.size();
Q_ASSERT(nbETAs); Q_ASSERT(nbETAs);
foreach(val, listEtas) { foreach(val, listEtas) {
moy += (qlonglong)((double)val/(double)nbETAs); moy += (qlonglong)((double)val/(double)nbETAs);
}
Q_ASSERT(moy >= 0); Q_ASSERT(moy >= 0);
}
ETAs[hash] = moy; ETAs[hash] = moy;
} else {
// Speed is too low, we don't want an overflow.
if(ETAstats.contains(hash)) {
ETAstats.remove(hash);
}
if(ETAs.contains(hash)) {
ETAs.remove(hash);
} }
} }
} }
}
// Delete big ratios
if(max_ratio != -1)
deleteBigRatios();
} }
long bittorrent::getETA(QString hash) const{ long bittorrent::getETA(QString hash) const{
@ -165,9 +213,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());
@ -185,6 +233,10 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
ETAs.remove(hash); ETAs.remove(hash);
// Remove tracker errors // Remove tracker errors
trackersErrors.remove(hash); trackersErrors.remove(hash);
// Remove from reloadingTorrents if reloading
if(reloadingTorrents.contains(hash)) {
reloadingTorrents.remove(hash);
}
// Remove it from ratio table // Remove it from ratio table
ratioData.remove(hash); ratioData.remove(hash);
int index = finishedTorrents.indexOf(hash); int index = finishedTorrents.indexOf(hash);
@ -198,11 +250,11 @@ 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);
} }
} }
@ -330,7 +382,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 +398,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 +410,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 +452,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 +466,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 +494,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
@ -538,13 +582,68 @@ 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) {
s->start_upnp();
} else {
s->stop_upnp();
}
}
void bittorrent::enableNATPMP(bool b) {
if(b) {
s->start_natpmp();
} else {
s->stop_natpmp();
}
}
void bittorrent::enableLSD(bool b) {
if(b) {
s->start_lsd();
} else {
s->stop_lsd();
}
}
// 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 +651,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) {
@ -660,7 +763,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;
} }
@ -865,6 +972,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 +989,15 @@ 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.;
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");
@ -980,6 +1101,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);
} }
@ -1012,10 +1134,9 @@ void bittorrent::readAlerts() {
if(index != -1){ if(index != -1){
waitingForPause.removeAt(index); waitingForPause.removeAt(index);
} }
index = reloadingTorrents.indexOf(hash); if(reloadingTorrents.contains(hash)) {
if(index != -1) { reloadTorrent(h, reloadingTorrents.value(hash));
reloadingTorrents.removeAt(index); reloadingTorrents.remove(hash);
reloadTorrent(h);
} }
} }
} }
@ -1061,7 +1182,7 @@ QStringList bittorrent::getTorrentsToPauseAfterChecking() const{
// Function to reload the torrent async after the torrent is actually // Function to reload the torrent async after the torrent is actually
// paused so that we can get fastresume data // paused so that we can get fastresume data
void bittorrent::pauseAndReloadTorrent(QTorrentHandle h) { void bittorrent::pauseAndReloadTorrent(QTorrentHandle h, bool full_alloc) {
if(!h.is_valid()) { if(!h.is_valid()) {
std::cerr << "/!\\ Error: Invalid handle\n"; std::cerr << "/!\\ Error: Invalid handle\n";
return; return;
@ -1069,14 +1190,14 @@ void bittorrent::pauseAndReloadTorrent(QTorrentHandle h) {
// ask to pause the torrent (async) // ask to pause the torrent (async)
h.pause(); h.pause();
QString hash = h.hash(); QString hash = h.hash();
// Add it to reloadingTorrents list so that we now we // Add it to reloadingTorrents has table so that we now we
// we should reload the torrent once we receive the // we should reload the torrent once we receive the
// torrent_paused_alert. pause() is async now... // torrent_paused_alert. pause() is async now...
reloadingTorrents << hash; reloadingTorrents[hash] = full_alloc;
} }
// 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 +1207,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 +1225,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
@ -1226,7 +1356,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 +1372,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");
} }

View file

@ -27,6 +27,7 @@
#include <QStringList> #include <QStringList>
#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;
@ -47,7 +48,7 @@ class bittorrent : public QObject{
downloadThread *downloader; downloadThread *downloader;
QString defaultSavePath; QString defaultSavePath;
QStringList torrentsToPauseAfterChecking; QStringList torrentsToPauseAfterChecking;
QStringList reloadingTorrents; QHash<QString, bool> reloadingTorrents;
QHash<QString, QList<qlonglong> > ETAstats; QHash<QString, QList<qlonglong> > ETAstats;
QHash<QString, qlonglong> ETAs; QHash<QString, qlonglong> ETAs;
QHash<QString, QPair<size_type,size_type> > ratioData; QHash<QString, QPair<size_type,size_type> > ratioData;
@ -57,6 +58,11 @@ class bittorrent : public QObject{
QStringList waitingForPause; QStringList waitingForPause;
QStringList finishedTorrents; QStringList finishedTorrents;
QStringList unfinishedTorrents; QStringList unfinishedTorrents;
bool preAllocateAll;
bool addInPause;
int maxConnecsPerTorrent;
int maxUploadsPerTorrent;
float max_ratio;
protected: protected:
QString getSavePath(QString hash); QString getSavePath(QString hash);
@ -84,22 +90,20 @@ class bittorrent : public QObject{
bool has_filtered_files(QString hash) const; bool has_filtered_files(QString hash) 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 disableDHT();
void saveDHTEntry(); void saveDHTEntry();
void preAllocateAllFiles(bool b);
void saveFastResumeAndRatioData(); void saveFastResumeAndRatioData();
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(ip_filter filter);
void disableIPFilter(); void disableIPFilter();
void pauseAndReloadTorrent(QTorrentHandle h);
void resumeUnfinishedTorrents(); void resumeUnfinishedTorrents();
void updateETAs(); void updateETAs();
void saveTorrentSpeedLimits(QString hash); void saveTorrentSpeedLimits(QString hash);
@ -111,12 +115,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 +132,10 @@ 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);
protected slots: protected slots:
void scanDirectory(); void scanDirectory();
@ -131,7 +143,9 @@ 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 pauseAndReloadTorrent(QTorrentHandle h, bool full_alloc);
void reloadTorrent(const QTorrentHandle &h, bool full_alloc); // This is protected now, call pauseAndReloadTorrent() instead
void deleteBigRatios();
signals: signals:
void invalidTorrent(QString path); void invalidTorrent(QString path);
@ -151,7 +165,7 @@ 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_deleted(QString hash, QString fileName, bool finished);
}; };
#endif #endif

View file

@ -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>590</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,108 @@
</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> </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 +510,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 +535,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>

View file

@ -42,51 +42,23 @@ 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);
show(); show();
} }
void createtorrent::on_browse_destination_clicked(){
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");
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 +66,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,
@ -147,60 +140,68 @@ 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.isEmpty()){
QMessageBox::critical(0, tr("No destination path set"), tr("Please type a destination path first")); QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first"));
return; return;
} }
QStringList input = allItems(input_list); QStringList trackers = allItems(trackers_list);
if(input.size() == 0){ if(!trackers.size()){
QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first")); 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; return;
} }
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;
path full_path;
ofstream out(complete(path((const char*)destination.toUtf8())), std::ios_base::binary); ofstream out(complete(path((const char*)destination.toUtf8())), std::ios_base::binary);
foreach(input_path, input){ path full_path;
full_path = complete(path((const char*)input_path.toUtf8())); // Adding files to the torrent
add_files(t, full_path.branch_path(), full_path.leaf()); full_path = complete(path(input.toUtf8().data()));
} add_files(*t, full_path.branch_path(), full_path.leaf());
int piece_size = 256 * 1024; // Set piece size
t.set_piece_size(piece_size); int piece_size = getPieceSize();
t->set_piece_size(piece_size);
// Add url seeds // Add url seeds
QStringList urlSeeds = allItems(URLSeeds_list); QStringList urlSeeds = allItems(URLSeeds_list);
QString seed; QString seed;
foreach(seed, urlSeeds){ foreach(seed, urlSeeds){
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());
} }
// 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());
} }
// 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*)txt_comment->toPlainText().toUtf8());
// Is private ? // Is private ?
if(check_private->isChecked()){ if(check_private->isChecked()){
t.set_priv(true); t->set_priv(true);
} }
// 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();
if(checkStartSeeding->isChecked())
emit torrent_to_seed(destination);
} }
catch (std::exception& e){ catch (std::exception& e){
std::cerr << e.what() << "\n"; std::cerr << e.what() << "\n";

View file

@ -30,13 +30,15 @@ class createtorrent : public QDialog, private Ui::createTorrentDialog{
public: public:
createtorrent(QWidget *parent = 0); createtorrent(QWidget *parent = 0);
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();

View file

@ -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;

174
src/downloadThread.cpp Normal file
View file

@ -0,0 +1,174 @@
/*
* 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 <cc++/common.h>
QString subDownloadThread::errorCodeToString(int status) {
switch(status){
case 1://ost::URLStream::errUnreachable:
return tr("Host is unreachable");
case 2://ost::URLStream::errMissing:
return tr("File was not found (404)");
case 3://ost::URLStream::errDenied:
return tr("Connection was denied");
case 4://ost::URLStream::errInvalid:
return tr("Url is invalid");
case 5://ost::URLStream::errForbidden:
return tr("Connection forbidden (403)");
case 6://ost::URLStream::errUnauthorized:
return tr("Connection was not authorized (401)");
case 7://ost::URLStream::errRelocated:
return tr("Content has moved (301)");
case 8://ost::URLStream::errFailure:
return tr("Connection failure");
case 9://ost::URLStream::errTimeout:
return tr("Connection was timed out");
case 10://ost::URLStream::errInterface:
return tr("Incorrect network interface");
default:
return tr("Unknown error");
}
}
subDownloadThread::subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){
url_stream = new ost::URLStream();
}
subDownloadThread::~subDownloadThread(){
abort = true;
wait();
delete url_stream;
}
void subDownloadThread::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;
}
ost::URLStream::Error status = url_stream->get((const char*)url.toUtf8());
if(status){
// Failure
QString error_msg = errorCodeToString((int)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());
}
/** 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();
}

View file

@ -29,55 +29,22 @@
#include <QMutexLocker> #include <QMutexLocker>
#include <QWaitCondition> #include <QWaitCondition>
#include <QStringList> #include <QStringList>
#include <iostream>
#include <cc++/common.h>
#ifdef CCXX_NAMESPACES namespace ost {
using namespace std; class URLStream;
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; ost::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(int 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 +52,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 +71,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);
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);
subThreads.removeAt(index); void propagateDownloadFailure(subDownloadThread* st, QString url, QString reason);
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

View file

@ -182,7 +182,7 @@ void DownloadingTorrents::deleteTorrent(QString hash) {
} }
// 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 +190,8 @@ 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>")); qDebug("Color is %s", color.name().toUtf8().data());
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) {
@ -246,7 +247,7 @@ void DownloadingTorrents::displayDLListMenu(const QPoint& pos) {
// 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")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
QString previewProgram = settings.value(QString::fromUtf8("Options/Misc/PreviewProgram"), QString()).toString(); QString previewProgram = settings.value(QString::fromUtf8("Preferences/general/MediaPlayer"), 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) {
@ -336,7 +337,7 @@ void DownloadingTorrents::displayInfoBarMenu(const QPoint& pos) {
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() {
@ -372,6 +373,10 @@ 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) {
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,10 +389,8 @@ 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"));
}
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:
@ -414,7 +417,7 @@ void DownloadingTorrents::updateDlList() {
}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);
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, QPalette::WindowText);
} }
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress())); DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate())); DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate()));
@ -661,10 +664,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 +684,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);
} }

View file

@ -76,13 +76,13 @@ 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);
public slots: public slots:
void updateDlList(); void updateDlList();
void setInfoBar(QString info, QString color="black"); void setInfoBar(QString info, QColor color=QPalette::WindowText);
void pauseTorrent(QString hash); void pauseTorrent(QString hash);
void resumeTorrent(QString hash); void resumeTorrent(QString hash);
void updateRatio(); void updateRatio();

125
src/engineSelect.ui Normal file
View 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: &lt;a href="http:plugins.qbittorrent.org">http://plugins.qbittorrent.org&lt;/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>

697
src/engineSelectDlg.cpp Normal file
View file

@ -0,0 +1,697 @@
/*
* 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 << "--supported_engines";
nova.start(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py", params, QIODevice::ReadOnly);
nova.waitForStarted();
nova.waitForFinished();
QByteArray result = nova.readAll();
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 << "--supported_engines";
nova.start(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py", params, QIODevice::ReadOnly);
nova.waitForStarted();
nova.waitForFinished();
QByteArray result = nova.readAll();
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 << "--supported_engines_infos";
nova.start(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py", params, QIODevice::ReadOnly);
nova.waitForStarted();
nova.waitForFinished();
result = nova.readAll();
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
View 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

View file

@ -12,7 +12,7 @@
<file>Icons/downarrow.png</file> <file>Icons/downarrow.png</file>
<file>Icons/description.png</file> <file>Icons/description.png</file>
<file>Icons/qbittorrent16.png</file> <file>Icons/qbittorrent16.png</file>
<file>Icons/exec.png</file> <file>Icons/file.png</file>
<file>Icons/systemtray.png</file> <file>Icons/systemtray.png</file>
<file>Icons/unhappy.png</file> <file>Icons/unhappy.png</file>
<file>Icons/filter.png</file> <file>Icons/filter.png</file>
@ -24,7 +24,7 @@
<file>Icons/style.png</file> <file>Icons/style.png</file>
<file>Icons/wizard.png</file> <file>Icons/wizard.png</file>
<file>Icons/password.png</file> <file>Icons/password.png</file>
<file>Icons/rss.png</file> <file>Icons/gear.png</file>
<file>Icons/sphere2.png</file> <file>Icons/sphere2.png</file>
<file>Icons/smile.png</file> <file>Icons/smile.png</file>
<file>Icons/loading.png</file> <file>Icons/loading.png</file>
@ -36,6 +36,18 @@
<file>Icons/add_file.png</file> <file>Icons/add_file.png</file>
<file>Icons/home.png</file> <file>Icons/home.png</file>
<file>Icons/splash.png</file> <file>Icons/splash.png</file>
<file>Icons/rss32.png</file>
<file>Icons/rss16.png</file>
<file>Icons/unsubscribe.png</file>
<file>Icons/subscribe.png</file>
<file>Icons/unsubscribe16.png</file>
<file>Icons/subscribe16.png</file>
<file>Icons/bt_settings.png</file>
<file>Icons/star.png</file>
<file>Icons/configure.png</file>
<file>Icons/download.png</file>
<file>Icons/folder.png</file>
<file>Icons/edit_clear.png</file>
<file>Icons/flags/portugal.png</file> <file>Icons/flags/portugal.png</file>
<file>Icons/flags/france.png</file> <file>Icons/flags/france.png</file>
<file>Icons/flags/ukraine.png</file> <file>Icons/flags/ukraine.png</file>
@ -46,7 +58,6 @@
<file>Icons/flags/slovakia.png</file> <file>Icons/flags/slovakia.png</file>
<file>Icons/flags/spain.png</file> <file>Icons/flags/spain.png</file>
<file>Icons/flags/finland.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/spain_catalunya.png</file>
<file>Icons/flags/poland.png</file> <file>Icons/flags/poland.png</file>
<file>Icons/flags/hungary.png</file> <file>Icons/flags/hungary.png</file>

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more