- Tagged rc4

This commit is contained in:
Christophe Dumez 2007-10-06 20:05:55 +00:00
commit 37bcb56bb8
150 changed files with 35041 additions and 17545 deletions

View file

@ -1,5 +1,5 @@
Author:
* Christophe Dumez <chris@qbittorrent.org>
Other developers:
Contributors:
* Arnaud Demaizière <arnaud@qbittorrent.org>

View file

@ -5,6 +5,7 @@
- FEATURE: Bittorrent FAST extension support
- FEATURE: Added RSS support
- FEATURE: Support files prioritizing in a torrent
- FEATURE: Brand new search engine plugins system
- FEATURE: Finished torrents are now moved to another tab for seeding
- FEATURE: Display more infos about the torrent in its properties
- FEATURE: Allow the user to edit torrents' trackers
@ -30,6 +31,15 @@
- FEATURE: User is now warned when fast resume data was rejected
- FEATURE: Url seeds are now displayed in torrent properties and are editable
- 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 Brazilian translation
- BUGFIX: Progress of paused torrents is now correct on restart
@ -52,11 +62,14 @@
- BUGFIX: Made torrent deletion from hard-drive safer
- BUGFIX: Prevent downloadFromUrl flooding
- BUGFIX: ETA was wrong for torrents with filtered files
- BUGFIX: Fixed drag'n drop on non-KDE systems
- COSMETIC: Redesigned torrent properties a little
- COSMETIC: Redesigned options a little
- COSMETIC: Totally redesigned program preferences
- COSMETIC: Display more logs messages concerning features
- COSMETIC: Improved lists renderers
- 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
- 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 ***
@ -14,11 +14,12 @@ qbittorrent
will install and execute qBittorrent hopefully without any problems.
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
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
on sourceforge (created by Arvid Norberg). The two libraries conflicts with each other.
@ -26,13 +27,17 @@ Dependencies:
- libcommoncpp2
- python >= 2.3 (previous might work - not tested): needed by search engine.
- python >= 2.3 (needed by search engine)
- libmagick++ (advised, not required)
* Needed for favicons support (RSS / Search plugins)
NOTE FOR GNOME USERS:
- qt4-qtconfig package is advised when using Plastique style (default)
or just change qBittorrent style to Cleanlooks (GNOME like)
- libzzip (advised, not required)
* Needed for zip support (Search plugins)
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:
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:
********************************
qBittorrent is a bittorrent client programmed in C++ / Qt4 that use
libtorrent (sometimes called rb_libtorrent) by Arvid Norberg.
qBittorrent is a bittorrent client programmed in C++ / Qt4 that uses
libtorrent (sometimes called rblibtorrent) by Arvid Norberg.
It aims to be a good alternative to all other bittorrent clients
out there. qBittorrent is fast, stable and provides unicode
support.
support as well as many features.
Installation:
********************************
For installation follow the instructions from INSTALL file, but simple
For installation, follow the instructions from INSTALL file, but simple:
./configure
make && make install
qbittorrent
will install and execute qBittorrent hopefully without any problems.
will install and execute qBittorrent hopefully without any problem.
For more information please visit:
http://www.qbittorrent.org
Please report any bug (or feature requests) to:
Please report any bug (or feature request) to:
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
------------------------------------------

103
TODO
View file

@ -1,98 +1,65 @@
// Easy
- Translations into as many languages as possible
- Improve man page
- Use Launchpad/Rosetta for translations once it supports TS files
// Intermediate
- Port on MacOS, Windows (and create an installer for Windows) - Progressing slowly
- Add some transparency (menus,...), improve look
- Port on MacOS, Windows (and create an installer for Windows) - Slow progress
- Add some transparency (menus,...), improve look / usabilty
- Skins support? (contact Mateusz)
// 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
- File selection in a torrent in compact mode
- Allow to prioritize torrents
- Allow to prioritize torrents (may code this in qBittorrent?)
// 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?
- 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?
(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
- Allow user to organize the downloads into categories/folders
- Allow user to organize the downloads into categories/folders?
// in v1.1.0
- Tabs support in search
- Allow to hide columns?
- 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)
- support zipped torrents? (useful?)
- Add option for RSS customization (refresh interval, max news per RSS...)
- Allow to disable UPnP/NAT-PMP/LSD in options?
- Allow to automatically delete torrents when they reach a given ratio (in options) : easy
- Allow to limit the number of downloading torrents simultaneously (other are paused until a download finishes)
- Add "Mark all as read" feature for RSS
- Allow to customize lists refreshing interval (in options)
- Use search engines as plugins (split them, load them dynamically) to allow the user to add some
- Improve search plugin install (choose in a list taken from plugins.qbittorrent.org)
- Display the number of DHT node if possible
- 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)
- Allow to change action on double-click
-> in download list
-> in seeding list
// in v1.0.0 (partial) - WIP
- Check storage st creation + hasher in torrent creation
// in v1.0.0 - FEATURE FREEZE
- Fix all (or almost all) opened bugs in bug tracker
- update sorting when a new torrent is added?
- Keep documention up to date
- Windows port (Chris - Peerkoel)
- write patches libtorrent for file_priority(int index), actual_size() ?
- valgrind --tool=memcheck --leak-check=full src/qbittorrent (Looks ok)
- 128m 29m 16m S 4.8 2.9 0:02.28 qbittorrent
* beta 6
- debug new torrent content selection
- Recheck doc
- Translations update (IN PROGRESS)
- Wait for some bug fixes in libtorrent :
- Number of seeds non null for finished torrent (Ticket #122)
- add qt4-qtconfig as package dependency
LANGUAGES UPDATED:
- French *BETA3*
- English *BETA3*
- Japanese *BETA3*
- Swedish *BETA3*
- Slovak *BETA3*
- Ukrainian *BETA3*
- Chinese (simplified) *BETA4*
- Hungarian *BETA4*
- Italian *BETA5*
- Polish *BETA5*
- Portuguese *BETA5*
- Brazilian *BETA5*
- Spanish *BETA5*
- German *BETA5*
- Russian *BETA5*
- Korean *BETA5*
- Greek *BETA6*
- Dutch *BETA6*
- Romanian *BETA6*
beta5->beta6 changelog:
- FEATURE: Split download tab from GUI class and cleaned up code
- FEATURE: A lot of code optimization (CPU & memory usage)
- FEATURE: Added support for .ico format (useful for RSS favicons)
- FEATURE: Replaced Meganova search engine by TorrentReactor
- I18N: Updated Greek, Dutch and Romanian translation
- I18N: Removed no longer maintained Traditional chinese translation
- BUGFIX: Made torrent deletion from hard-drive safer
- BUGFIX: Fixed a bug when switching from finished to downloading list
- BUGFIX: Showing checking progress for paused torrents too
- BUGFIX: Fixed progress column sorting on startup
- BUGFIX: Prevent downloadFromUrl flooding
- BUGFIX: Fixed pause state toggle for paused and checking torrents
- BUGFIX: Made finished list context menu more similar to the download list one
- BUGFIX: Fixed Pause/Start action in lists context menus
- BUGFIX: Improved ETA calculation
- BUGFIX: ETA was wrong for torrents with filtered files
- BUGFIX: Display the torrent that are being checked as 'checking' in seeding list
- BUGFIX: Fixed file preview and improved previewable files detection
- BUGFIX: Do not store and calculate ETA values for finished/paused torrents
- BUGFIX: Fixed memory leak in GUI
rc3->rc4 changelog:
- BUGFIX: Fixed ip filter preferences (couldn't enable it)
- BUGFIX: Fixed compilation problems on FreeBSD (Ok now)
- BUGFIX: Updated INSTALL file
- BUGFIX: Optimized torrent real size calculation
- BUGFIX: Use system default style as a default (instead of Plastique style)

114
configure vendored
View file

@ -27,6 +27,9 @@ Dependency options:
--disable-libmagick Disable use of libmagick
--with-libmagick-inc=[path] Path to libmagick++ include 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
}
@ -188,6 +191,21 @@ while [ $# -gt 0 ]; do
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)
QC_VERBOSE="Y"
shift
@ -218,6 +236,9 @@ echo QC_WITH_LIBCOMMONCPP2_LIB=$QC_WITH_LIBCOMMONCPP2_LIB
echo QC_DISABLE_libmagick=$QC_DISABLE_libmagick
echo QC_WITH_LIBMAGICK_INC=$QC_WITH_LIBMAGICK_INC
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
fi
@ -319,21 +340,21 @@ fi
gen_files() {
cat >$1/modules.cpp <<EOT
#line 1 "qt42.qcm"
#line 1 "qt4.qcm"
/*
-----BEGIN QCMOD-----
name: Qt >= 4.2
name: Qt >= 4.3
-----END QCMOD-----
*/
class qc_qt42 : public ConfObj
class qc_qt4 : public ConfObj
{
public:
qc_qt42(Conf *c) : ConfObj(c) {}
QString name() const { return "Qt >= 4.2"; }
QString shortname() const { return "qt42"; }
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 >= 0x040200);
return(QT_VERSION >= 0x040300);
}
};
#line 1 "libtorrent.qcm"
@ -618,11 +639,82 @@ public:
magickConfig.waitForStarted();
magickConfig.waitForFinished();
QByteArray result = magickConfig.readAll();
result = result.replace("\n", "");
conf->addLib(result.data());
conf->addDefine("HAVE_MAGICK");
return true;
}
};
#line 1 "libzzip.qcm"
/*
-----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/local/lib/";
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;
}
};
#line 1 "python.qcm"
/*
-----BEGIN QCMOD-----
@ -646,7 +738,7 @@ public:
EOT
cat >$1/modules_new.cpp <<EOT
o = new qc_qt42(conf);
o = new qc_qt4(conf);
o->required = true;
o->disabled = false;
o = new qc_libtorrent(conf);
@ -661,6 +753,9 @@ cat >$1/modules_new.cpp <<EOT
o = new qc_libmagick(conf);
o->required = false;
o->disabled = false;
o = new qc_libzzip(conf);
o->required = false;
o->disabled = false;
o = new qc_python(conf);
o->required = true;
o->disabled = false;
@ -1618,6 +1713,9 @@ export QC_WITH_LIBCOMMONCPP2_LIB
export QC_DISABLE_libmagick
export QC_WITH_LIBMAGICK_INC
export QC_WITH_LIBMAGICK_LIB
export QC_DISABLE_libzzip
export QC_WITH_LIBZZIP_INC
export QC_WITH_LIBZZIP_LIB
export QC_VERBOSE
rm -rf .qconftemp
(

Binary file not shown.

View file

@ -3,7 +3,7 @@
<profile>qbittorrent.pro</profile>
<moddir>qcm</moddir>
<datadir/>
<dep type='qt42'>
<dep type='qt4'>
<required/>
</dep>
<dep type='libtorrent'>
@ -16,6 +16,7 @@
<required/>
</dep>
<dep type='libmagick'/>
<dep type='libzzip'/>
<dep type='python'>
<required/>
</dep>

View file

@ -69,6 +69,7 @@ public:
magickConfig.waitForStarted();
magickConfig.waitForFinished();
QByteArray result = magickConfig.readAll();
result = result.replace("\n", "");
conf->addLib(result.data());
conf->addDefine("HAVE_MAGICK");
return true;

69
qcm/libzzip.qcm Normal file
View file

@ -0,0 +1,69 @@
/*
-----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/local/lib/";
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;
}
};

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

@ -86,7 +86,8 @@ class DLListDelegate: public QItemDelegate {
newopt.textVisible = false;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt,
painter);
painter->setPen(QColor("Black"));
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
painter->setPen(opt.palette.color(cg, QPalette::WindowText));
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
break;
}

View file

@ -79,7 +79,8 @@ class FinishedListDelegate: public QItemDelegate {
newopt.textVisible = false;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt,
painter);
painter->setPen(QColor("Black"));
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
painter->setPen(opt.palette.color(cg, QPalette::WindowText));
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
break;
}

View file

@ -122,7 +122,8 @@ void FinishedTorrents::torrentAdded(QString, QTorrentHandle& h, bool) {
// Set the color of a row in data model
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);
}
}
@ -209,6 +210,10 @@ void FinishedTorrents::updateFinishedList(){
}
Q_ASSERT(row != -1);
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.)) {
// What are you doing here? go back to download tab!
qDebug("Info: a torrent was moved from finished to download tab");
@ -218,10 +223,8 @@ void FinishedTorrents::updateFinishedList(){
continue;
}
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);
setRowColor(row, QString::fromUtf8("grey"));
}
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)h.progress()));
continue;
}
@ -311,7 +314,7 @@ void FinishedTorrents::displayFinishedListMenu(const QPoint& pos){
// Enable/disable pause/start action given the DL state
QModelIndexList selectedIndexes = finishedList->selectionModel()->selectedIndexes();
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;
foreach(index, selectedIndexes) {
if(index.column() == F_NAME) {

View file

@ -56,11 +56,11 @@ namespace fs = boost::filesystem;
*****************************************************/
// 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);
setWindowTitle(tr("qBittorrent %1", "e.g: qBittorrent v0.x").arg(QString::fromUtf8(VERSION)));
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
if (QSystemTrayIcon::isSystemTrayAvailable()) {
if(systrayIntegration) {
@ -112,6 +112,7 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
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(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");
tabs = new QTabWidget();
// Download torrents tab
@ -130,6 +131,10 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
// Smooth torrent switching between tabs Downloading <--> Finished
connect(downloadingTorrentTab, SIGNAL(torrentFinished(QString)), finishedTorrentTab, 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
configureSession(true);
// Resume unfinished torrents
@ -141,7 +146,7 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
// RSS tab
rssWidget = new RSSImp();
tabs->addTab(rssWidget, tr("RSS"));
tabs->setTabIcon(3, QIcon(QString::fromUtf8(":/Icons/rss.png")));
tabs->setTabIcon(3, QIcon(QString::fromUtf8(":/Icons/rss32.png")));
readSettings();
// Add torrent given on command line
processParams(torrentCmdLine);
@ -155,10 +160,6 @@ GUI::GUI(QWidget *parent, QStringList torrentCmdLine) : QMainWindow(parent), for
checkConnect = new QTimer(this);
connect(checkConnect, SIGNAL(timeout()), this, SLOT(checkConnectionStatus()));
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);
connect(previewProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(cleanTempPreviewFile(int, QProcess::ExitStatus)));
// Accept drag 'n drops
@ -243,7 +244,7 @@ void GUI::finishedTorrent(QTorrentHandle& h) const {
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
bool show_msg = true;
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
QString hash = h.hash();
if(QFile::exists(misc::qBittorrentPath()+QString::fromUtf8("BT_backup")+QDir::separator()+hash+QString::fromUtf8(".finished"))) {
@ -254,7 +255,7 @@ void GUI::finishedTorrent(QTorrentHandle& h) const {
downloadingTorrentTab->setInfoBar(tr("%1 has finished downloading.", "e.g: xxx.avi has finished downloading.").arg(fileName));
downloadingTorrentTab->deleteTorrent(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);
}
}
@ -262,8 +263,8 @@ void GUI::finishedTorrent(QTorrentHandle& h) const {
// Notification when disk is full
void GUI::fullDiskError(QTorrentHandle& h) const {
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
int useOSD = settings.value(QString::fromUtf8("Options/OSDEnabled"), 1).toInt();
if(systrayIntegration && (useOSD == 1 || (useOSD == 2 && (isMinimized() || isHidden())))) {
bool useNotificationBalloons = settings.value(QString::fromUtf8("Preferences/General/NotificationBaloons"), true).toBool();
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);
}
// Download will be paused by libtorrent. Updating GUI information accordingly
@ -412,7 +413,7 @@ void GUI::previewFile(QString filePath) {
QStringList params;
params << tmpPath;
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);
}else{
QMessageBox::critical(0, tr("Preview process already running"), tr("There is already another preview process running.\nPlease close the other one first."));
@ -475,13 +476,13 @@ void GUI::on_actionAbout_triggered() {
void GUI::closeEvent(QCloseEvent *e) {
qDebug("Mainwindow received closeEvent");
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()) {
hide();
e->ignore();
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();
if(!isMaximized())
showNormal();
@ -511,13 +512,14 @@ void GUI::closeEvent(QCloseEvent *e) {
// Display window to create a torrent
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 path)));
}
// Called when we minimize the program
void GUI::hideEvent(QHideEvent *e) {
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() && !e->spontaneous()) {
// Hide window
hide();
}
@ -528,13 +530,25 @@ void GUI::hideEvent(QHideEvent *e) {
// Action executed when a file is dropped
void GUI::dropEvent(QDropEvent *event) {
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
QString file;
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) {
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());
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);
@ -553,7 +567,11 @@ void GUI::dropEvent(QDropEvent *event) {
// Decode if we accept drag 'n drop or not
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();
}
}
@ -567,16 +585,14 @@ void GUI::dragEnterEvent(QDragEnterEvent *event) {
// Display a dialog to allow user to add
// torrents to download list
void GUI::on_actionOpen_triggered() {
QStringList pathsList;
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
// Open File Open Dialog
// 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("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
if(!pathsList.empty()) {
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();
unsigned int listSize = pathsList.size();
for(unsigned int i=0; i<listSize; ++i) {
if(useTorrentAdditionDialog) {
@ -646,6 +662,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
void GUI::on_actionDelete_triggered() {
QStringList hashes;
@ -708,7 +734,7 @@ void GUI::on_actionDelete_triggered() {
void GUI::processParams(const QStringList& params) {
QString param;
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) {
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)) {
@ -717,7 +743,7 @@ void GUI::processParams(const QStringList& params) {
if(useTorrentAdditionDialog) {
torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
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);
}else{
BTSession->addTorrent(param);
@ -726,15 +752,19 @@ void GUI::processParams(const QStringList& params) {
}
}
void GUI::addTorrent(QString path) {
BTSession->addTorrent(path);
}
void GUI::processScannedFiles(const QStringList& params) {
QString param;
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) {
if(useTorrentAdditionDialog) {
torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
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);
}else{
BTSession->addTorrent(param, true);
@ -744,11 +774,11 @@ void GUI::processScannedFiles(const QStringList& params) {
void GUI::processDownloadedFiles(QString path, QString url) {
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) {
torrentAdditionDialog *dialog = new torrentAdditionDialog(this);
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);
}else{
BTSession->addTorrent(path, false, url);
@ -758,92 +788,67 @@ void GUI::processDownloadedFiles(QString path, QString url) {
// Set BT session configuration
void GUI::configureSession(bool deleteOptions) {
qDebug("Configuring session");
QPair<int, int> limits;
unsigned short old_listenPort, new_listenPort;
proxy_settings proxySettings;
session_settings sessionSettings;
pe_settings encryptionSettings;
// Configure session regarding options
// General
displaySpeedInTitle = options->speedInTitleBar();
unsigned int new_refreshInterval = options->getRefreshInterval();
if(refreshInterval != new_refreshInterval) {
refreshInterval = new_refreshInterval;
refresher->start(refreshInterval);
}
// Downloads
// * Save path
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());
new_listenPort = BTSession->getListenPort();
unsigned short new_listenPort = BTSession->getListenPort();
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)));
}
// Apply max connec limit (-1 if disabled)
BTSession->setMaxConnections(options->getMaxConnec());
limits = options->getLimits();
switch(limits.first) {
case -1: // Download limit disabled
case 0:
// * Global download limit
QPair<int, int> limits = options->getGlobalBandwidthLimits();
if(limits.first <= 0) {
// Download limit disabled
BTSession->setDownloadRateLimit(-1);
break;
default:
} else {
// Enabled
BTSession->setDownloadRateLimit(limits.first*1024);
}
switch(limits.second) {
case -1: // Upload limit disabled
case 0:
// * Global Upload limit
if(limits.second <= 0) {
// Upload limit disabled
BTSession->setUploadRateLimit(-1);
break;
default:
} else {
// Enabled
BTSession->setUploadRateLimit(limits.second*1024);
}
// Apply ratio (0 if disabled)
BTSession->setGlobalRatio(options->getRatio());
// DHT (Trackerless)
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
// * UPnP
if(options->isUPnPEnabled()) {
BTSession->enableUPnP(true);
downloadingTorrentTab->setInfoBar(tr("UPnP support [ON]"), QString::fromUtf8("blue"));
// Encryption settings
int encryptionState = options->getEncryptionSetting();
// The most secure, rc4 only so that all streams and encrypted
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);
// PeX
if(!options->isPeXDisabled()) {
qDebug("Enabling Peer eXchange (PeX)");
downloadingTorrentTab->setInfoBar(tr("PeX support [ON]"), QString::fromUtf8("blue"));
BTSession->enablePeerExchange();
} else {
downloadingTorrentTab->setInfoBar(tr("PeX support [OFF]"), QString::fromUtf8("blue"));
qDebug("Peer eXchange (PeX) disabled");
BTSession->enableUPnP(false);
downloadingTorrentTab->setInfoBar(tr("UPnP support [OFF]"), QString::fromUtf8("blue"));
}
// Apply filtering settings
if(options->isFilteringEnabled()) {
BTSession->enableIPFilter(options->getFilter());
downloadingTorrentTab->setBottomTabEnabled(1, true);
// * NAT-PMP
if(options->isNATPMPEnabled()) {
BTSession->enableNATPMP(true);
downloadingTorrentTab->setInfoBar(tr("NAT-PMP support [ON]"), QString::fromUtf8("blue"));
} else {
BTSession->disableIPFilter();
downloadingTorrentTab->setBottomTabEnabled(1, false);
BTSession->enableNATPMP(false);
downloadingTorrentTab->setInfoBar(tr("NAT-PMP support [OFF]"), QString::fromUtf8("blue"));
}
// Apply Proxy settings
// * Proxy settings
proxy_settings proxySettings;
if(options->isProxyEnabled()) {
switch(options->getProxyType()) {
case HTTP:
@ -867,14 +872,79 @@ void GUI::configureSession(bool deleteOptions) {
}
}
BTSession->setProxySettings(proxySettings, options->useProxyForTrackers(), options->useProxyForPeers(), options->useProxyForWebseeds(), options->useProxyForDHT());
// * Session settings
session_settings sessionSettings;
sessionSettings.user_agent = "qBittorrent "VERSION;
BTSession->setSessionSettings(sessionSettings);
// Scan dir stuff
if(options->getScanDir().isNull()) {
BTSession->disableDirectoryScanning();
// Bittorrent
// * Max connections limit
BTSession->setMaxConnections(options->getMaxConnecs());
// * Max connections per torrent limit
BTSession->setMaxConnectionsPerTorrent(options->getMaxConnecsPerTorrent());
// * Max uploads per torrent limit
BTSession->setMaxUploadsPerTorrent(options->getMaxUploadsPerTorrent());
// * DHT
if(options->isDHTEnabled()) {
BTSession->enableDHT(true);
downloadingTorrentTab->setInfoBar(tr("DHT support [ON], port: %1").arg(new_listenPort), QString::fromUtf8("blue"));
// Set DHT Port
BTSession->setDHTPort(new_listenPort);
}else{
BTSession->enableDirectoryScanning(options->getScanDir());
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) {
delete options;
}
@ -919,22 +989,39 @@ void GUI::togglePausedState(QString hash) {
void GUI::on_actionPause_All_triggered() {
bool change = false;
bool inDownloadList = true;
if(tabs->currentIndex() > 1) return;
if(tabs->currentIndex() == 1)
bool hidden = false;
switch(getCurrentTabIndex()) {
case -1:
hidden = true;
inDownloadList = false;
QStringList hashes;
if(inDownloadList) {
hashes = BTSession->getUnfinishedTorrents();
} else {
hashes = BTSession->getFinishedTorrents();
break;
case 0:
break;
case 1:
inDownloadList = false;
break;
default:
return;
}
QStringList DL_hashes;
QStringList F_hashes;
if(hidden || inDownloadList) {
DL_hashes = BTSession->getUnfinishedTorrents();
}
if(hidden || !inDownloadList) {
F_hashes = BTSession->getFinishedTorrents();
}
QString hash;
foreach(hash, hashes) {
foreach(hash, DL_hashes) {
if(BTSession->pauseTorrent(hash)){
change = true;
if(inDownloadList)
downloadingTorrentTab->pauseTorrent(hash);
else
}
}
foreach(hash, F_hashes) {
if(BTSession->pauseTorrent(hash)){
change = true;
finishedTorrentTab->pauseTorrent(hash);
}
}
@ -971,22 +1058,39 @@ void GUI::on_actionPause_triggered() {
void GUI::on_actionStart_All_triggered() {
bool change = false;
bool inDownloadList = true;
if(tabs->currentIndex() > 1) return;
if(tabs->currentIndex() == 1)
bool hidden = false;
switch(getCurrentTabIndex()) {
case -1:
hidden = true;
inDownloadList = false;
QStringList hashes;
if(inDownloadList) {
hashes = BTSession->getUnfinishedTorrents();
} else {
hashes = BTSession->getFinishedTorrents();
break;
case 0:
break;
case 1:
inDownloadList = false;
break;
default:
return;
}
QStringList DL_hashes;
QStringList F_hashes;
if(hidden || inDownloadList) {
DL_hashes = BTSession->getUnfinishedTorrents();
}
if(hidden || !inDownloadList) {
F_hashes = BTSession->getFinishedTorrents();
}
QString hash;
foreach(hash, hashes) {
foreach(hash, DL_hashes) {
if(BTSession->resumeTorrent(hash)){
change = true;
if(inDownloadList)
downloadingTorrentTab->resumeTorrent(hash);
else
}
}
foreach(hash, F_hashes) {
if(BTSession->resumeTorrent(hash)){
change = true;
finishedTorrentTab->resumeTorrent(hash);
}
}
@ -1049,6 +1153,11 @@ void GUI::updateLists() {
default:
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
@ -1067,7 +1176,16 @@ void GUI::checkConnectionStatus() {
downloadingTorrentTab->updateRatio();
// update global informations
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();
if(sessionStatus.has_incoming_connections) {
@ -1134,7 +1252,7 @@ void GUI::on_actionOptions_triggered() {
// Is executed each time options are saved
void GUI::OptionsSaved(QString info, bool deleteOptions) {
bool newSystrayIntegration = options->useSystrayIntegration();
bool newSystrayIntegration = options->systrayIntegration();
if(newSystrayIntegration && !systrayIntegration) {
// create the trayicon
createTrayIcon();

View file

@ -64,7 +64,9 @@ class GUI : public QMainWindow, private Ui::MainWindow{
FinishedTorrents *finishedTorrentTab;
QLabel *connecStatusLblIcon;
bool systrayIntegration;
bool displaySpeedInTitle;
bool force_exit;
unsigned int refreshInterval;
QTimer *refresher;
// Keyboard shortcuts
QShortcut *switchSearchShortcut;
@ -127,10 +129,12 @@ class GUI : public QMainWindow, private Ui::MainWindow{
void checkConnectionStatus();
void configureSession(bool deleteOptions);
void processParams(const QStringList& params);
void addTorrent(QString path);
void addUnauthenticatedTracker(QPair<QTorrentHandle,QString> tracker);
void processScannedFiles(const QStringList& params);
void processDownloadedFiles(QString path, QString url);
void downloadFromURLList(const QStringList& urls);
void deleteTorrent(QString hash, QString fileName, bool finished);
void finishedTorrent(QTorrentHandle& h) const;
void torrentChecked(QString hash) const;
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

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
Exec=qbittorrent
GenericName=Bittorrent client
GenericName[fr]=Client Bittorrent
GenericName[nl]=Bittorrent client
GenericName[bg]=Торент клиент
GenericName[de]=Bittorren Client
GenericName[el]=Τορεντ πελάτης
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[tr]=Bittorrent istemcisi
GenericName[de]=Bittorren Client
GenericName[pl]=Klient Bittorrent
GenericName[zh]=Bittorrent
GenericName[ko]=
GenericName[el]=Τορεντ πελάτης
GenericName[bg]=Торент клиент
GenericName[uk]=Bittorrent-клієнт
GenericName[ru]=клиент Bittorrent
GenericName[zh]=Bittorrent
Icon=qbittorrent
MimeType=application/x-bittorrent
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

@ -63,7 +63,8 @@ class PreviewListDelegate: public QItemDelegate {
newopt.state |= QStyle::State_Enabled;
newopt.textVisible = false;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
painter->setPen(QColor("Black"));
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
painter->setPen(opt.palette.color(cg, QPalette::WindowText));
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
break;
}

View file

@ -38,6 +38,7 @@
#define SIZE 1
#define PROGRESS 2
#define PRIORITY 3
#define INDEX 4
#define IGNORED 0
#define NORMAL 1
@ -76,7 +77,8 @@ class PropListDelegate: public QItemDelegate {
newopt.state |= QStyle::State_Enabled;
newopt.textVisible = false;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
painter->setPen(QColor("Black"));
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
painter->setPen(opt.palette.color(cg, QPalette::WindowText));
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
break;
}
@ -115,7 +117,6 @@ class PropListDelegate: public QItemDelegate {
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const {
if(index.column() != PRIORITY) return 0;
if(onlyOneItem(index)) return 0;
QComboBox* editor = new QComboBox(parent);
editor->setFocusPolicy(Qt::StrongFocus);
editor->addItem(tr("Ignored"));
@ -128,6 +129,7 @@ class PropListDelegate: public QItemDelegate {
void setEditorData(QWidget *editor, const QModelIndex &index) const {
unsigned short val = index.model()->data(index, Qt::DisplayRole).toInt();
QComboBox *combobox = static_cast<QComboBox*>(editor);
qDebug("Set Editor data: Prio is %d", val);
switch(val){
case IGNORED:
combobox->setCurrentIndex(0);
@ -156,28 +158,11 @@ class PropListDelegate: public QItemDelegate {
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:
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
QComboBox *combobox = static_cast<QComboBox*>(editor);
int value = combobox->currentIndex();
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();
switch(value){
case 0:
@ -185,6 +170,10 @@ class PropListDelegate: public QItemDelegate {
model->setData(index, QVariant(IGNORED));
if(filteredFilesChanged != 0)
*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;
case 1:
@ -192,6 +181,9 @@ class PropListDelegate: public QItemDelegate {
model->setData(index, QVariant(NORMAL));
if(filteredFilesChanged != 0)
*filteredFilesChanged = true;
} else {
model->setData(index, QVariant(HIGH));
model->setData(index, QVariant(NORMAL));
}
break;
case 2:
@ -199,6 +191,9 @@ class PropListDelegate: public QItemDelegate {
model->setData(index, QVariant(HIGH));
if(filteredFilesChanged != 0)
*filteredFilesChanged = true;
} else {
model->setData(index, QVariant(NORMAL));
model->setData(index, QVariant(HIGH));
}
break;
case 3:
@ -206,6 +201,9 @@ class PropListDelegate: public QItemDelegate {
model->setData(index, QVariant(MAXIMUM));
if(filteredFilesChanged != 0)
*filteredFilesChanged = true;
} else {
model->setData(index, QVariant(HIGH));
model->setData(index, QVariant(MAXIMUM));
}
break;
default:
@ -213,16 +211,17 @@ class PropListDelegate: public QItemDelegate {
model->setData(index, QVariant(NORMAL));
if(filteredFilesChanged != 0)
*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 {
editor->setGeometry(option.rect);
}
};
#endif

View file

@ -44,9 +44,9 @@ class about : public QDialog, private Ui::AboutDlg{
// Thanks
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("<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 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 am gratefull to Peter Koeleman (peter@qbittorrent.org) who is helping port qBittorrent to Windows.</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, RPM packager, for his help and support.</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->scrollToAnchor(QString::fromUtf8("top"));
// Translation

View file

@ -5,20 +5,29 @@
<rect>
<x>0</x>
<y>0</y>
<width>440</width>
<height>389</height>
<width>511</width>
<height>441</height>
</rect>
</property>
<property name="windowTitle" >
<string>Torrent addition dialog</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</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>
<widget class="QLabel" name="fileNameLbl" >
<property name="text" >
@ -48,12 +57,21 @@
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</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>
<widget class="QLineEdit" name="savePathTxt" />
</item>
@ -108,12 +126,21 @@
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</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>
<spacer>
<property name="orientation" >

View file

@ -157,13 +157,13 @@ class BandwidthAllocationDialog : public QDialog, private Ui_bandwidth_dlg {
s->set_upload_rate_limit(-1);
else
s->set_upload_rate_limit(val*1024);
settings.setValue(QString::fromUtf8("Options/Main/UPLimit"), val);
settings.setValue(QString::fromUtf8("Preferences/Connection/GlobalUPLimit"), val);
}else{
if(!val)
s->set_download_rate_limit(-1);
else
s->set_download_rate_limit(val*1024);
settings.setValue(QString::fromUtf8("Options/Main/DLLimit"), val);
settings.setValue(QString::fromUtf8("Preferences/Connection/GlobalDLLimit"), val);
}
}
close();

256
src/arborescence.h Normal file
View file

@ -0,0 +1,256 @@
/*
* 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);
float sum = 0;
file *child;
foreach(child, children) {
sum += child->getProgress();
}
progress = sum / (float)children.size();
}
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 <QTimer>
#include "bittorrent.h"
#include "misc.h"
#include "downloadThread.h"
#include "deleteThread.h"
#include <libtorrent/extensions/metadata_transfer.hpp>
#include <libtorrent/extensions/ut_pex.hpp>
#include <libtorrent/entry.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/identify_client.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/ip_filter.hpp>
#include <libtorrent/torrent_info.hpp>
#include <boost/filesystem/exception.hpp>
#include "bittorrent.h"
#include "misc.h"
#include "downloadThread.h"
#include "deleteThread.h"
#define ETAS_MAX_VALUES 3
#define ETA_REFRESH_INTERVAL 10000
#define MAX_TRACKER_ERRORS 2
// 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
fs::path::default_name_check(fs::no_check);
// Creating bittorrent session
s = new session(fingerprint("qB", VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, 0));
// Set severity level of libtorrent session
s->set_severity_level(alert::info);
// Enable LSD/UPnP/NAT-PMP
s->start_lsd();
s->start_natpmp();
s->start_upnp();
// Enabling metadata plugin
s->add_extension(&create_metadata_plugin);
timerAlerts = new QTimer();
@ -85,6 +80,43 @@ bittorrent::~bittorrent() {
delete s;
}
void bittorrent::preAllocateAllFiles(bool b) {
preAllocateAll = b;
if(b) {
// Reload All 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;
}
QString hash = h.hash();
if(has_filtered_files(hash)) continue;
reloadTorrent(h);
}
}
}
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) {
QTorrentHandle h = getTorrentHandle(hash);
if(h.is_valid())
@ -104,6 +136,10 @@ void bittorrent::handleDownloadFailure(QString url, QString reason) {
emit downloadFromUrlFailure(url, reason);
}
void bittorrent::startTorrentsInPause(bool b) {
addInPause = b;
}
void bittorrent::updateETAs() {
QString hash;
foreach(hash, unfinishedTorrents) {
@ -118,19 +154,26 @@ void bittorrent::updateETAs() {
if(h.download_payload_rate()) {
listEtas << (qlonglong)((h.actual_size()-h.total_done())/(double)h.download_payload_rate());
ETAstats[hash] = listEtas;
long moy = 0;
long val;
qlonglong moy = 0;
qlonglong val;
unsigned int nbETAs = listEtas.size();
Q_ASSERT(nbETAs);
foreach(val, listEtas) {
moy += (qlonglong)((double)val/(double)nbETAs);
Q_ASSERT(moy >= 0);
if(moy < 0) break;
}
if(moy < 0) {
ETAs.remove(hash);
} else {
ETAs[hash] = moy;
}
}
}
}
// Delete big ratios
if(max_ratio != -1)
deleteBigRatios();
}
long bittorrent::getETA(QString hash) const{
return ETAs.value(hash, -1);
@ -165,9 +208,9 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
}
QString savePath = h.save_path();
QString fileName = h.name();
QStringList files_path;
arborescence *files_arb = 0;
if(permanent){
files_path = h.files_path();
files_arb = new arborescence(h.get_torrent_info());
}
// Remove it from session
s->remove_torrent(h.get_torrent_handle());
@ -198,11 +241,11 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
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
qDebug("Removing this on hard drive: %s", qPrintable(savePath+QDir::separator()+fileName));
// Deleting in a thread to avoid GUI freeze
deleter->deleteTorrent(savePath, files_path);
deleter->deleteTorrent(savePath, files_arb);
}
}
@ -346,11 +389,11 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
}
}
// Processing torrents
file = path.trimmed().replace("file://", "");
file = path.trimmed().replace("file://", "", Qt::CaseInsensitive);
if(file.isEmpty()) {
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());
std::ifstream in(file.toUtf8().data(), std::ios_base::binary);
in.unsetf(std::ios_base::skipws);
@ -358,27 +401,18 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
// Decode torrent file
entry e = bdecode(std::istream_iterator<char>(in), std::istream_iterator<char>());
// Getting torrent file informations
torrent_info t(e);
qDebug(" -> Hash: %s", misc::toString(t.info_hash()).c_str());
qDebug(" -> Name: %s", t.name().c_str());
QString hash = misc::toQString(t.info_hash());
boost::intrusive_ptr<torrent_info> t(new torrent_info(e));
qDebug(" -> Hash: %s", misc::toString(t->info_hash()).c_str());
qDebug(" -> Name: %s", t->name().c_str());
QString hash = misc::toQString(t->info_hash());
if(file.startsWith(torrentBackup.path())) {
QFileInfo fi(file);
QString old_hash = fi.baseName();
if(old_hash != hash){
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");
// Update info Bar
if(!fromScanDir) {
@ -409,7 +443,7 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
}
QString savePath = getSavePath(hash);
// Adding files to bittorrent session
if(has_filtered_files(hash)) {
if(has_filtered_files(hash) || preAllocateAll) {
h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, false, true);
qDebug(" -> Full allocation mode");
}else{
@ -423,9 +457,10 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
if(!from_url.isNull()) QFile::remove(file);
return;
}
// Is this really useful and appropriate ?
//h.set_max_connections(60);
h.set_max_uploads(-1);
// Connections limit per torrent
h.set_max_connections(maxConnecsPerTorrent);
// Uploads limit per torrent
h.set_max_uploads(maxUploadsPerTorrent);
// Load filtered files
loadFilesPriorities(h);
// Load custom url seeds
@ -450,13 +485,13 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
QFile::copy(file, newFile);
}
// Pause torrent if it was paused last time
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) {
if(addInPause || QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) {
torrentsToPauseAfterChecking << hash;
qDebug("Adding a torrent to the torrentsToPauseAfterChecking list");
}
// Incremental download
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);
}
// Start torrent because it was added in paused state
@ -538,13 +573,68 @@ void bittorrent::setMaxConnections(int 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
bool bittorrent::isDHTEnabled() const{
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
void bittorrent::enableDHT() {
void bittorrent::enableDHT(bool b) {
if(b) {
if(!DHTEnabled) {
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);
@ -559,16 +649,14 @@ void bittorrent::enableDHT() {
DHTEnabled = true;
qDebug("DHT enabled");
}
}
// Disable DHT
void bittorrent::disableDHT() {
} else {
if(DHTEnabled) {
DHTEnabled = false;
s->stop_dht();
qDebug("DHT disabled");
}
}
}
void bittorrent::saveTorrentSpeedLimits(QString hash) {
qDebug("Saving speedLimits file for %s", hash.toUtf8().data());
@ -660,7 +748,11 @@ void bittorrent::loadDownloadUploadForTorrent(QString hash) {
QPair<size_type,size_type> downUp;
downUp.first = (size_type)data_list.at(0).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;
}
@ -865,6 +957,11 @@ void bittorrent::setUploadRateLimit(long rate) {
// libtorrent allow to adjust ratio for each torrent
// This function will apply to same ratio to all torrents
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();
unsigned int nbHandles = handles.size();
for(unsigned int i=0; i<nbHandles; ++i) {
@ -877,6 +974,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) {
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers");
@ -980,6 +1086,7 @@ void bittorrent::readAlerts() {
}
else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a.get())) {
QTorrentHandle h(p->handle);
qDebug("File Error: %s", p->msg().c_str());
if(h.is_valid())
emit fullDiskError(h);
}
@ -1086,7 +1193,7 @@ void bittorrent::reloadTorrent(const QTorrentHandle &h) {
fs::path saveDir = h.save_path_boost();
QString fileName = h.name();
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());
entry resumeData;
// Checking if torrentBackup Dir exists
@ -1109,8 +1216,10 @@ void bittorrent::reloadTorrent(const QTorrentHandle &h) {
}
QTorrentHandle new_h = s->add_torrent(t, saveDir, resumeData, false);
qDebug("Using full allocation mode");
new_h.set_max_uploads(-1);
// Connections limit per torrent
new_h.set_max_connections(maxConnecsPerTorrent);
// Uploads limit per torrent
new_h.set_max_uploads(maxUploadsPerTorrent);
// Load filtered Files
loadFilesPriorities(new_h);
// Load speed limit from hard drive

View file

@ -27,6 +27,7 @@
#include <QStringList>
#include <libtorrent/session.hpp>
#include <libtorrent/ip_filter.hpp>
#include "qtorrenthandle.h"
using namespace libtorrent;
@ -57,6 +58,11 @@ class bittorrent : public QObject{
QStringList waitingForPause;
QStringList finishedTorrents;
QStringList unfinishedTorrents;
bool preAllocateAll;
bool addInPause;
int maxConnecsPerTorrent;
int maxUploadsPerTorrent;
float max_ratio;
protected:
QString getSavePath(QString hash);
@ -90,9 +96,8 @@ class bittorrent : public QObject{
void deleteTorrent(QString hash, bool permanent = false);
bool pauseTorrent(QString hash);
bool resumeTorrent(QString hash);
void enableDHT();
void disableDHT();
void saveDHTEntry();
void preAllocateAllFiles(bool b);
void saveFastResumeAndRatioData();
void enableDirectoryScanning(QString scan_dir);
void disableDirectoryScanning();
@ -111,12 +116,16 @@ class bittorrent : public QObject{
// Session configuration - Setters
void setListeningPortsRange(std::pair<unsigned short, unsigned short> ports);
void setMaxConnections(int maxConnec);
void setMaxConnectionsPerTorrent(int max);
void setMaxUploadsPerTorrent(int max);
void setDownloadRateLimit(long rate);
void setUploadRateLimit(long rate);
void setGlobalRatio(float ratio);
void setDeleteRatio(float ratio);
void setDHTPort(int dht_port);
void setProxySettings(proxy_settings proxySettings, bool trackers=true, bool peers=true, bool web_seeds=true, bool dht=true);
void setSessionSettings(session_settings sessionSettings);
void startTorrentsInPause(bool b);
void setDefaultSavePath(QString savepath);
void applyEncryptionSettings(pe_settings se);
void loadFilesPriorities(QTorrentHandle& h);
@ -124,6 +133,10 @@ class bittorrent : public QObject{
void setUploadLimit(QString hash, long val);
void setUnfinishedTorrent(QString hash);
void setFinishedTorrent(QString hash);
void enableUPnP(bool b);
void enableNATPMP(bool b);
void enableLSD(bool b);
void enableDHT(bool b);
protected slots:
void scanDirectory();
@ -132,6 +145,7 @@ class bittorrent : public QObject{
bool loadTrackerFile(QString hash);
void saveTrackerFile(QString hash);
void reloadTorrent(const QTorrentHandle &h); // This is protected now, call pauseAndReloadTorrent() instead
void deleteBigRatios();
signals:
void invalidTorrent(QString path);
@ -151,7 +165,7 @@ class bittorrent : public QObject{
void fastResumeDataRejected(QString name);
void urlSeedProblem(QString url, QString msg);
void torrentFinishedChecking(QString hash);
void torrent_deleted(QString hash, QString fileName, bool finished);
};
#endif

View file

@ -5,22 +5,16 @@
<rect>
<x>0</x>
<y>0</y>
<width>605</width>
<height>588</height>
<width>592</width>
<height>590</height>
</rect>
</property>
<property name="windowTitle" >
<string>Torrent Creation Tool</string>
</property>
<layout class="QVBoxLayout" >
<item>
<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" >
<size>
<width>0</width>
@ -51,49 +45,45 @@
<set>Qt::AlignCenter</set>
</property>
</widget>
<widget class="QWidget" name="layoutWidget" >
<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" >
<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" >
<string>Input files or directories:</string>
<string>File or folder to add to the torrent:</string>
</property>
</widget>
</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>
<widget class="QLabel" name="lbl_announce_url" >
<property name="minimumSize" >
@ -155,129 +145,22 @@
</item>
<item>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QListWidget" name="input_list" >
<property name="selectionMode" >
<enum>QAbstractItemView::MultiSelection</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" >
<property name="margin" >
<property name="leftMargin" >
<number>0</number>
</property>
<property name="spacing" >
<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" >
<property name="topMargin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item>
<widget class="QListWidget" name="trackers_list" >
@ -288,12 +171,21 @@
</item>
<item>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</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>
<spacer>
<property name="orientation" >
@ -324,6 +216,9 @@
<property name="text" >
<string/>
</property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/skin/add.png</iconset>
</property>
</widget>
</item>
<item>
@ -343,6 +238,9 @@
<property name="text" >
<string/>
</property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/skin/remove.png</iconset>
</property>
</widget>
</item>
<item>
@ -364,12 +262,21 @@
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</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>
<widget class="QListWidget" name="URLSeeds_list" >
<property name="selectionMode" >
@ -379,12 +286,21 @@
</item>
<item>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</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>
<spacer>
<property name="orientation" >
@ -415,6 +331,9 @@
<property name="text" >
<string/>
</property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/skin/add.png</iconset>
</property>
</widget>
</item>
<item>
@ -434,6 +353,9 @@
<property name="text" >
<string/>
</property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/skin/remove.png</iconset>
</property>
</widget>
</item>
<item>
@ -455,12 +377,6 @@
</item>
<item>
<widget class="QTextEdit" name="txt_comment" >
<property name="minimumSize" >
<size>
<width>421</width>
<height>102</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>421</width>
@ -475,89 +391,108 @@
</layout>
</item>
</layout>
</widget>
<widget class="QCheckBox" name="check_private" >
<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>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
<item>
<widget class="QLabel" name="txtPieceSize" >
<property name="text" >
<string>Piece size:</string>
</property>
<property name="spacing" >
<number>6</number>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboPieceSize" >
<property name="currentIndex" >
<number>3</number>
</property>
<item>
<widget class="QLabel" name="lbl_destination" >
<property name="maximumSize" >
<property name="text" >
<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>
<width>16777215</width>
<height>26</height>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="check_private" >
<property name="text" >
<string>Destination torrent file:</string>
<string>Private (won't be distributed on DHT network if enabled)</string>
</property>
<property name="buddy" >
<cstring>txt_destination</cstring>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkStartSeeding" >
<property name="text" >
<string>Start seeding after creation</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<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" >
<property name="leftMargin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item>
<spacer>
@ -575,7 +510,7 @@
<item>
<widget class="QPushButton" name="createButton" >
<property name="text" >
<string>Create</string>
<string>Create and save...</string>
</property>
</widget>
</item>
@ -600,9 +535,12 @@
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<resources>
<include location="icons.qrc" />
</resources>
<connections>
<connection>
<sender>cancelButton</sender>

View file

@ -42,48 +42,20 @@ using namespace boost::filesystem;
createtorrent::createtorrent(QWidget *parent): QDialog(parent){
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);
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(){
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)
input_list->addItem(dir);
if(!dir.isEmpty())
textInputPath->setText(dir);
}
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;
foreach(file, files){
if(input_list->findItems(file, Qt::MatchCaseSensitive).size() == 0){
input_list->addItem(file);
}
}
}
void createtorrent::on_removeFolder_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;
}
QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), QDir::homePath(), QString(), 0, QFileDialog::ShowDirsOnly);
if(!file.isEmpty())
textInputPath->setText(file);
}
void createtorrent::on_removeTracker_button_clicked() {
@ -94,6 +66,27 @@ void createtorrent::on_removeTracker_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;
QString URL = QInputDialog::getText(this, tr("Please type an announce URL"),
@ -147,60 +140,67 @@ QStringList createtorrent::allItems(QListWidget *list){
// Main function that create a .torrent file
void createtorrent::on_createButton_clicked(){
QString destination = txt_destination->text();
if(destination.isEmpty()){
QMessageBox::critical(0, tr("No destination path set"), tr("Please type a destination path first"));
QString input = textInputPath->text().trimmed();
if(input.isEmpty() == 0){
QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first"));
return;
}
QStringList input = allItems(input_list);
if(input.size() == 0){
QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first"));
QStringList trackers = allItems(trackers_list);
if(!trackers.size()){
QMessageBox::critical(0, tr("No tracker path set"), tr("Please set at least one tracker"));
return;
}
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
if(!destination.isEmpty()) {
if(!destination.endsWith(QString::fromUtf8(".torrent")))
destination += QString::fromUtf8(".torrent");
} else {
return;
}
char const* creator_str = "qBittorrent "VERSION;
try {
torrent_info t;
QString input_path;
path full_path;
boost::intrusive_ptr<torrent_info> t(new torrent_info);
ofstream out(complete(path((const char*)destination.toUtf8())), std::ios_base::binary);
foreach(input_path, input){
full_path = complete(path((const char*)input_path.toUtf8()));
add_files(t, full_path.branch_path(), full_path.leaf());
}
int piece_size = 256 * 1024;
t.set_piece_size(piece_size);
path full_path;
// Adding files to the torrent
full_path = complete(path(input.toUtf8().data()));
add_files(*t, full_path.branch_path(), full_path.leaf());
// Set piece size
int piece_size = getPieceSize();
t->set_piece_size(piece_size);
// Add url seeds
QStringList urlSeeds = allItems(URLSeeds_list);
QString seed;
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){
t.add_tracker((const char*)trackers.at(i).toUtf8());
t->add_tracker(trackers.at(i).toUtf8().data());
}
// calculate the hash for all pieces
file_pool 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);
for (int i = 0; i < num; ++i) {
st->read(&buf[0], i, 0, t.piece_size(i));
hasher h(&buf[0], t.piece_size(i));
t.set_hash(i, h.final());
st->read(&buf[0], i, 0, t->piece_size(i));
hasher h(&buf[0], t->piece_size(i));
t->set_hash(i, h.final());
}
// Set qBittorrent as creator and add user comment to
// torrent_info structure
t.set_creator(creator_str);
t.set_comment((const char*)txt_comment->toPlainText().toUtf8());
t->set_creator(creator_str);
t->set_comment((const char*)txt_comment->toPlainText().toUtf8());
// Is private ?
if(check_private->isChecked()){
t.set_priv(true);
t->set_priv(true);
}
// 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);
if(checkStartSeeding->isChecked())
emit torrent_to_seed(destination);
}
catch (std::exception& e){
std::cerr << e.what() << "\n";

View file

@ -30,13 +30,15 @@ class createtorrent : public QDialog, private Ui::createTorrentDialog{
public:
createtorrent(QWidget *parent = 0);
QStringList allItems(QListWidget *list);
int getPieceSize() const;
signals:
void torrent_to_seed(QString path);
protected slots:
void on_browse_destination_clicked();
void on_createButton_clicked();
void on_addFile_button_clicked();
void on_addFolder_button_clicked();
void on_removeFolder_button_clicked();
void on_addTracker_button_clicked();
void on_removeTracker_button_clicked();
void on_addURLSeed_button_clicked();

View file

@ -28,17 +28,17 @@
#include <QMutexLocker>
#include <QPair>
#include "misc.h"
#include "arborescence.h"
class subDeleteThread : public QThread {
Q_OBJECT
private:
QString save_path;
QStringList files_path;
arborescence *arb;
bool abort;
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(){
abort = true;
@ -52,10 +52,11 @@ class subDeleteThread : public QThread {
protected:
void run(){
if(misc::removeTorrentSavePath(save_path, files_path))
if(arb->removeFromFS(save_path))
emit deletionSuccessST(this);
else
emit deletionFailureST(this);
delete arb;
}
};
@ -63,7 +64,7 @@ class deleteThread : public QThread {
Q_OBJECT
private:
QList<QPair<QString, QStringList> > torrents_list;
QList<QPair<QString, arborescence*> > torrents_list;
QMutex mutex;
QWaitCondition condition;
bool abort;
@ -81,9 +82,10 @@ class deleteThread : public QThread {
wait();
}
void deleteTorrent(QString save_path, QStringList files_path){
void deleteTorrent(QString saveDir, arborescence *arb){
qDebug("deleteThread called");
QMutexLocker locker(&mutex);
torrents_list << QPair<QString, QStringList>(save_path, files_path);
torrents_list << QPair<QString, arborescence*>(saveDir, arb);
if(!isRunning()){
start();
}else{
@ -98,7 +100,7 @@ class deleteThread : public QThread {
return;
mutex.lock();
if(torrents_list.size() != 0){
QPair<QString, QStringList> torrent = torrents_list.takeFirst();
QPair<QString, arborescence *> torrent = torrents_list.takeFirst();
mutex.unlock();
subDeleteThread *st = new subDeleteThread(0, torrent.first, torrent.second);
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 <QWaitCondition>
#include <QStringList>
#include <iostream>
#include <cc++/common.h>
#ifdef CCXX_NAMESPACES
using namespace std;
using namespace ost;
#endif
namespace ost {
class URLStream;
}
class subDownloadThread : public QThread {
Q_OBJECT
private:
QString url;
URLStream url_stream;
ost::URLStream *url_stream;
bool abort;
public:
subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){}
~subDownloadThread(){
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");
}
}
subDownloadThread(QObject *parent, QString url);
~subDownloadThread();
QString errorCodeToString(int status);
signals:
// For subthreads
@ -85,47 +52,7 @@ class subDownloadThread : public QThread {
void downloadFailureST(subDownloadThread* st, QString url, QString reason);
protected:
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());
}
void run();
};
class downloadThread : public QThread {
@ -144,75 +71,19 @@ class downloadThread : public QThread {
void downloadFailure(QString url, QString reason);
public:
downloadThread(QObject* parent) : QThread(parent), abort(false){}
downloadThread(QObject* parent);
~downloadThread(){
mutex.lock();
abort = true;
condition.wakeOne();
mutex.unlock();
qDeleteAll(subThreads);
wait();
}
~downloadThread();
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();
}
}
void downloadUrl(QString url);
protected:
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 run();
void 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();
}
protected slots:
void propagateDownloadedFile(subDownloadThread* st, QString url, QString path);
void propagateDownloadFailure(subDownloadThread* st, QString url, QString reason);
};
#endif

View file

@ -182,7 +182,7 @@ void DownloadingTorrents::deleteTorrent(QString hash) {
}
// Update Info Bar information
void DownloadingTorrents::setInfoBar(QString info, QString color) {
void DownloadingTorrents::setInfoBar(QString info, QColor color) {
static unsigned int nbLines = 0;
++nbLines;
// Check log size, clear it if too big
@ -190,7 +190,8 @@ void DownloadingTorrents::setInfoBar(QString info, QString color) {
infoBar->clear();
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) {
@ -246,7 +247,7 @@ void DownloadingTorrents::displayDLListMenu(const QPoint& pos) {
// Enable/disable pause/start action given the DL state
QModelIndexList selectedIndexes = downloadList->selectionModel()->selectedIndexes();
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;
foreach(index, selectedIndexes) {
if(index.column() == NAME) {
@ -336,7 +337,7 @@ void DownloadingTorrents::displayInfoBarMenu(const QPoint& pos) {
QMenu myLogMenu(this);
myLogMenu.addAction(actionClearLog);
// XXX: Why mapToGlobal() is not enough?
myLogMenu.exec(mapToGlobal(pos)+QPoint(22,383));
myLogMenu.exec(mapToGlobal(pos)+QPoint(44,305));
}
void DownloadingTorrents::sortProgressColumnDelayed() {
@ -372,6 +373,10 @@ void DownloadingTorrents::updateDlList() {
Q_ASSERT(row != -1);
// No need to update a paused torrent
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
// Setting download state
switch(h.state()) {
@ -384,10 +389,8 @@ void DownloadingTorrents::updateDlList() {
continue;
case torrent_status::checking_files:
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);
setRowColor(row, QString::fromUtf8("grey"));
}
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
break;
case torrent_status::connecting_to_tracker:
@ -414,7 +417,7 @@ void DownloadingTorrents::updateDlList() {
}else{
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));
setRowColor(row, QString::fromUtf8("black"));
setRowColor(row, QPalette::WindowText);
}
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
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
void DownloadingTorrents::setRowColor(int row, QString color) {
unsigned int nbColumns = DLListModel->columnCount();
void DownloadingTorrents::setRowColor(int row, QColor color) {
unsigned int nbColumns = DLListModel->columnCount()-1;
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) {
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 torrentCorrupted(QString path);
void portListeningFailure();
void setRowColor(int row, QString color);
void setRowColor(int row, QColor color);
void displayDownloadingUrlInfos(QString url);
void showProperties(const QModelIndex &index);
public slots:
void updateDlList();
void setInfoBar(QString info, QString color="black");
void setInfoBar(QString info, QColor color=QPalette::WindowText);
void pauseTorrent(QString hash);
void resumeTorrent(QString hash);
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/description.png</file>
<file>Icons/qbittorrent16.png</file>
<file>Icons/exec.png</file>
<file>Icons/file.png</file>
<file>Icons/systemtray.png</file>
<file>Icons/unhappy.png</file>
<file>Icons/filter.png</file>
@ -24,7 +24,7 @@
<file>Icons/style.png</file>
<file>Icons/wizard.png</file>
<file>Icons/password.png</file>
<file>Icons/rss.png</file>
<file>Icons/gear.png</file>
<file>Icons/sphere2.png</file>
<file>Icons/smile.png</file>
<file>Icons/loading.png</file>
@ -36,6 +36,17 @@
<file>Icons/add_file.png</file>
<file>Icons/home.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/flags/portugal.png</file>
<file>Icons/flags/france.png</file>
<file>Icons/flags/ukraine.png</file>
@ -46,7 +57,6 @@
<file>Icons/flags/slovakia.png</file>
<file>Icons/flags/spain.png</file>
<file>Icons/flags/finland.png</file>
<file>Icons/flags/china_hong_kong.png</file>
<file>Icons/flags/spain_catalunya.png</file>
<file>Icons/flags/poland.png</file>
<file>Icons/flags/hungary.png</file>

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.

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