Compare commits

...

34 Commits

Author SHA1 Message Date
sledgehammer999
19adad5e43 Bump to 3.1.12 2015-02-23 00:14:24 +02:00
sledgehammer999
edcd9539e7 Update Changelog. 2015-02-23 00:14:17 +02:00
David Christenson
066c70b047 Adjust library paths, add deployment target 2015-02-23 00:14:09 +02:00
David Christenson
2b24018f7a Change library paths and remove prefix 2015-02-23 00:14:01 +02:00
sledgehammer999
c421da873f Remove openssl dependency from Mac OS X configuration file. 2015-02-23 00:13:53 +02:00
sledgehammer999
1ae49d1743 Fix heap corruption. Closes #2342.
Manual backporting of commit b45eb28099 (master).
2015-02-23 00:13:45 +02:00
sledgehammer999
aecb51c42b Improve Windows Registry searching for Python.
Manual backporting of commit f851875ad1 (master).
2015-02-23 00:13:37 +02:00
sledgehammer999
19c0a98701 WINDOWS: Fix automatic python download. Commit 0799dc2 broke this. Closes #2076. 2015-02-23 00:13:17 +02:00
sledgehammer999
ba1f9558a9 Fix compilation with libtorrent 0.15.x. 2014-11-26 02:48:49 +02:00
sledgehammer999
79f3c6439c Stop using internal libtorrent API. Closes 2202. 2014-11-26 01:56:29 +02:00
sledgehammer999
99a596ab8d Bump to 3.1.11 2014-10-22 22:32:25 +03:00
sledgehammer999
9a74b27a85 Update Changelog. 2014-10-22 22:28:00 +03:00
sledgehammer999
b12f250642 Use boost:bind() as the docs show. Allows compilation with older gcc versions.
Conflicts:
	src/torrentcreator/torrentcreatorthread.cpp
2014-10-22 22:02:08 +03:00
sledgehammer999
2dede108e7 Don't show multiple unlock UI dialogs. Closes #2040. 2014-10-22 22:00:41 +03:00
Ivan Sorokin
7cf1e7b8ca Optimize misc::naturalSort()
Previous implementation used QRegExp to find a first digit. That is
utterly ineffective.

When torrent list is sorted by name (a column that uses the naturalSort() for
comparison), naturalSort could take 18.6% of the time of the UI thread. Optimize it
so now it takes 1% of the time of the UI thread.
2014-10-22 22:00:27 +03:00
sledgehammer999
e1934e8c16 Use the correct character encoding for exceptions coming from libtorrent.
Conflicts:
	src/qtlibtorrent/qbtsession.cpp
2014-10-22 21:59:26 +03:00
Gabriele
14b958216b Increase maximum size of system icons
System icons were limited to a size 24x24 pixels, while the embedded
icons are 32x32 pixels big.
2014-10-22 21:56:58 +03:00
Gabriele
1120c14890 Make Windows icons suitable for high dpi screens
The maximum size of the icons is now 256x256 pixels so that
Windows can correctly scale them.

The 256x256 layer for the main icon was created upscaling the
192x192 png available in the source tree. Hence, the icon might
not be perfect at the maximum size.

The icon for the file association was made from scratch and it's
visually similar to the previous icon.
2014-10-22 21:56:44 +03:00
Gabriele
88075d9226 Allow relative torrent paths when qBittorrent is already running
Adding new torrents from the command line while qBittorrent was
already running was possible only providing the absolute path to
the file.
2014-10-22 21:56:29 +03:00
Gabriele
36464fcd59 Set minimum width of the left panel in the preferences
Change also the default width and set it to the minimum.
This minimum width prevents the horizontal scrollbar from appearing.

The size of the items in the list depends on the Qt style, so the
left panel could be few pixels larger than required with some of them.
2014-10-22 21:56:10 +03:00
Gabriele
f7f1c81238 Don't stretch the last section in the transfer list
Since the content of some sections is right aligned, automatically
resizing the width of the last one to fill the header could be
sometimes undesired.

Let the user choose the width of each section and never change his
preference.
2014-10-22 21:55:56 +03:00
Bruno Barbieri
b8da4bcf74 Fix search inconsistency between Python versions
Closes #2012
2014-10-22 21:55:41 +03:00
Gabriele
bf7a6aceb0 Remove unneeded tooltip 2014-10-22 21:55:24 +03:00
DoumanAsh
3ef2da898b Pirate bay search engine update 2014-10-22 21:55:10 +03:00
Bruno Barbieri
d0cd939143 Fix search engine encoding issues with python3 on Windows
Closes #1996
2014-10-22 21:54:50 +03:00
Bruno Barbieri
e36d76d457 Replace deprecated sgmllib with HTMLParser/html.parser 2014-10-22 21:54:38 +03:00
Bruno Barbieri
daa4314093 Fix TorrentReactor search plugin 2014-10-22 21:54:25 +03:00
sledgehammer999
f707d6c9d5 Merge pull request #2052 from pmzqla/v3_1_x-charset
WebUI: Set correct HTTP Content-Type in case of forbidden access
2014-10-19 17:27:20 +03:00
Gabriele
83b6619b16 WebUI: Set correct HTTP Content-Type in case of forbidden access
The error message is an utf-8 string. With no charset specified,
web browsers will likely use the wrong one making reading the message
in some languages impossibile.
2014-10-19 16:13:18 +02:00
sledgehammer999
8b322648c8 Change the program updater's URL for Windows and Mac OS X. Closes #1954.
Conflicts:
	src/programupdater.cpp
2014-10-12 16:43:27 +03:00
sledgehammer999
d159117965 WINDOWS: Fix magnet link association. Closes #1952. 2014-10-12 15:59:39 +03:00
sledgehammer999
1fd2dce0bd Fix Mac OS X compilation. 2014-10-06 02:36:03 +03:00
Ivan Sorokin
f97238e1c9 Fix heap-buffer-overrun in PropertiesWidget::displayFilesListMenu 2014-10-04 21:39:47 +03:00
paolo-sz
67355810ae Correctly detect python in PATH 2014-10-04 21:39:26 +03:00
37 changed files with 495 additions and 360 deletions

View File

@@ -1,3 +1,30 @@
* Wed Feb 22 2015 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.1.12
- OSX: Fix build to work with older machines. (sledgehammer999, Noctem)
- WINDOWS: Fix automatic Python download. (sledgehammer999)
- WINDOWS: Fix crashes due to memory corruption and improve Python registry searching. (glassez)
* Wed Oct 22 2014 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.1.11
- FEATURE: Allow relative torrent paths when qBittorrent is already running (pmzqla)
- FEATURE: Make Windows icons suitable for high dpi screens (pmzqla)
- FEATURE: Increase maximum size of system icons (pmzqla)
- BUGFIX: Fix crash in the "Content" widget when user would right click in it without a torrent selected (Ivan Sorokin)
- BUGFIX: Don't show multiple unlock UI dialogs. Closes #2040. (sledgehammer999)
- SEARCH: Fix bug where python would falsely be detected and nothing worked (paolo-sz)
- SEARCH: Fix TorrentReactor search plugin (Bruno Barbieri)
- SEARCH: Fix search engine encoding issues with python3 on Windows (Bruno Barbieri)
- SEARCH: Pirate bay search engine update (DoumanAsh)
- SEARCH: Internal improvements in the python code (Bruno Barbieri)
- WINDOWS: Fix magnet link association. Closes #1952. (sledgehammer999)
- WINDOWS and OSX: Fix again the program updater. The url was changed by sourceforge.net. Closes #1954. (sledgehammer999)
- OSX: Fix compilation (sledgehammer999)
- WEBUI: Set correct HTTP Content-Type in case of forbidden access. (pmzqla)
- COSMETIC: Remove unneeded tooltip (pmzqla)
- COSMETIC: Don't stretch the last section in the transfer list (pmzqla)
- COSMETIC: Set minimum width of the left panel in the preferences (pmzqla)
- OTHER: Optimize sorting of rows. This should have less CPU impact when many torrents are present. (Ivan Sorokin)
- OTHER: Use the correct character encoding for exceptions coming from libtorrent. (sledgehammer999)
- OTHER: Use boost:bind() as the docs show. Allows compilation with older gcc versions. (sledgehammer999)
* Sun Sep 21 2014 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.1.10 * Sun Sep 21 2014 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.1.10
- FEATURE: Allow disabling of OS cache. This will prevent RAM increases on Windows when seeding many files. Closes #1699. (sledgehammer999) - FEATURE: Allow disabling of OS cache. This will prevent RAM increases on Windows when seeding many files. Closes #1699. (sledgehammer999)
- FEATURE: Add 'Completed' column. Closes #1241. (sledgehammer999) - FEATURE: Add 'Completed' column. Closes #1241. (sledgehammer999)

View File

@@ -7,12 +7,10 @@ CONFIG += link_pkgconfig
PKGCONFIG += libtorrent-rasterbar PKGCONFIG += libtorrent-rasterbar
DEFINES += BOOST_ASIO_DYN_LINK DEFINES += BOOST_ASIO_DYN_LINK
# Special include/libs paths (macports) # Special include/libs paths (homebrew and macports)
INCLUDEPATH += /usr/include/openssl /usr/include /opt/local/include/boost /opt/local/include INCLUDEPATH += /usr/local/include /opt/local/include /usr/include
LIBS += -L/opt/local/lib LIBS += -L/usr/local/lib -L/opt/local/lib -L/usr/lib
# OpenSSL lib
LIBS += -lssl -lcrypto
# Boost system lib # Boost system lib
LIBS += -lboost_system-mt LIBS += -lboost_system-mt
# Boost filesystem lib (Not needed for libtorrent >= 0.16.0) # Boost filesystem lib (Not needed for libtorrent >= 0.16.0)
@@ -29,8 +27,9 @@ QMAKE_BUNDLE_DATA += document_icon
qt_conf.path = Contents/Resources qt_conf.path = Contents/Resources
qt_conf.files = mac/qt.conf qt_conf.files = mac/qt.conf
QMAKE_BUNDLE_DATA += qt_conf QMAKE_BUNDLE_DATA += qt_conf
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7
qt_translations.path = Contents/MacOS/translations qt_translations.path = Contents/translations
qt_translations.files = qt-translations/qt_ar.qm \ qt_translations.files = qt-translations/qt_ar.qm \
qt-translations/qt_bg.qm \ qt-translations/qt_bg.qm \
qt-translations/qt_ca.qm \ qt-translations/qt_ca.qm \

View File

@@ -197,7 +197,7 @@ bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path, const QString
m_torrentInfo = new torrent_info(m_filePath.toUtf8().data()); m_torrentInfo = new torrent_info(m_filePath.toUtf8().data());
m_hash = misc::toQString(m_torrentInfo->info_hash()); m_hash = misc::toQString(m_torrentInfo->info_hash());
} catch(const std::exception& e) { } catch(const std::exception& e) {
MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1").arg(e.what())); MessageBoxRaised::critical(0, tr("Invalid torrent"), tr("Failed to load the torrent: %1").arg(misc::toQStringU(e.what())));
return false; return false;
} }

View File

@@ -81,7 +81,7 @@ QIcon IconProvider::generateDifferentSizes(const QIcon& icon)
{ {
QIcon new_icon; QIcon new_icon;
QList<QSize> required_sizes; QList<QSize> required_sizes;
required_sizes << QSize(16, 16) << QSize(24, 24); required_sizes << QSize(16, 16) << QSize(24, 24) << QSize(32, 32);
QList<QIcon::Mode> modes; QList<QIcon::Mode> modes;
modes << QIcon::Normal << QIcon::Active << QIcon::Selected << QIcon::Disabled; modes << QIcon::Normal << QIcon::Active << QIcon::Selected << QIcon::Disabled;
foreach (const QSize& size, required_sizes) { foreach (const QSize& size, required_sizes) {

View File

@@ -45,7 +45,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.1.10</string> <string>3.1.12</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>qBit</string> <string>qBit</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
@@ -59,7 +59,7 @@
<key>NSAppleScriptEnabled</key> <key>NSAppleScriptEnabled</key>
<string>YES</string> <string>YES</string>
<key>NSHumanReadableCopyright</key> <key>NSHumanReadableCopyright</key>
<string>Copyright © 2006-2014 The qBittorrent project</string> <string>Copyright © 2006-2015 The qBittorrent project</string>
<key>UTExportedTypeDeclarations</key> <key>UTExportedTypeDeclarations</key>
<array> <array>
<dict> <dict>

View File

@@ -1,4 +1,3 @@
[Paths] [Paths]
Prefix = MacOS
Translations = translations Translations = translations
Plugins = PlugIns Plugins = PlugIns

View File

@@ -228,8 +228,13 @@ int main(int argc, char *argv[]) {
QStringList torrentCmdLine = app.arguments(); QStringList torrentCmdLine = app.arguments();
//Pass program parameters if any //Pass program parameters if any
QString message; QString message;
QFileInfo torrentPath;
for (int a = 1; a < torrentCmdLine.size(); ++a) { for (int a = 1; a < torrentCmdLine.size(); ++a) {
if (torrentCmdLine[a].startsWith("--")) continue; if (torrentCmdLine[a].startsWith("--")) continue;
torrentPath.setFile(torrentCmdLine[a]);
if (torrentPath.exists())
message += torrentPath.absoluteFilePath();
else
message += torrentCmdLine[a]; message += torrentCmdLine[a];
if (a < argc-1) if (a < argc-1)
message += "|"; message += "|";

10
src/mainwindow.cpp Normal file → Executable file
View File

@@ -101,7 +101,7 @@ using namespace libtorrent;
*****************************************************/ *****************************************************/
// Constructor // Constructor
MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine) : QMainWindow(parent), m_posInitialized(false), force_exit(false) MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine) : QMainWindow(parent), m_posInitialized(false), force_exit(false), unlockDlgShowing(false)
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
, has_python(false) , has_python(false)
#endif #endif
@@ -697,8 +697,14 @@ void MainWindow::setTabText(int index, QString text) const {
} }
bool MainWindow::unlockUI() { bool MainWindow::unlockUI() {
if (unlockDlgShowing)
return false;
else
unlockDlgShowing = true;
bool ok = false; bool ok = false;
QString clear_password = AutoExpandableDialog::getText(this, tr("UI lock password"), tr("Please type the UI lock password:"), QLineEdit::Password, "", &ok); QString clear_password = AutoExpandableDialog::getText(this, tr("UI lock password"), tr("Please type the UI lock password:"), QLineEdit::Password, "", &ok);
unlockDlgShowing = false;
if (!ok) return false; if (!ok) return false;
Preferences pref; Preferences pref;
QString real_pass_md5 = pref.getUILockPasswordMD5(); QString real_pass_md5 = pref.getUILockPasswordMD5();
@@ -1343,7 +1349,7 @@ void MainWindow::on_actionSearch_engine_triggered() {
bool res = false; bool res = false;
// Check if python is already in PATH // Check if python is already in PATH
if (misc::pythonVersion()) if (misc::pythonVersion() > 0)
res = true; res = true;
else else
res = addPythonPathToEnv(); res = addPythonPathToEnv();

View File

@@ -155,6 +155,8 @@ private:
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
bool addPythonPathToEnv(); bool addPythonPathToEnv();
void installPython(); void installPython();
private slots:
void pythonDownloadSuccess(QString url, QString file_path); void pythonDownloadSuccess(QString url, QString file_path);
void pythonDownloadFailure(QString url, QString error); void pythonDownloadFailure(QString url, QString error);
#endif #endif
@@ -183,6 +185,7 @@ private:
bool displaySpeedInTitle; bool displaySpeedInTitle;
bool force_exit; bool force_exit;
bool ui_locked; bool ui_locked;
bool unlockDlgShowing;
LineEdit *search_filter; LineEdit *search_filter;
// Keyboard shortcuts // Keyboard shortcuts
QShortcut *switchSearchShortcut; QShortcut *switchSearchShortcut;

View File

@@ -70,6 +70,8 @@ const int UNLEN = 256;
#endif #endif
#endif // DISABLE_GUI #endif // DISABLE_GUI
#include <libtorrent/magnet_uri.hpp>
using namespace libtorrent; using namespace libtorrent;
static struct { const char *source; const char *comment; } units[] = { static struct { const char *source; const char *comment; } units[] = {
@@ -136,8 +138,8 @@ void misc::shutdownComputer(shutDownAction action) {
} }
#endif #endif
#ifdef Q_WS_MAC #ifdef Q_WS_MAC
AEEventID EventToSend;
if (action != SHUTDOWN_COMPUTER) if (action != SHUTDOWN_COMPUTER)
if (sleep)
EventToSend = kAESleep; EventToSend = kAESleep;
else else
EventToSend = kAEShutDown; EventToSend = kAEShutDown;
@@ -372,6 +374,7 @@ QList<QUrl> misc::magnetUriToTrackers(const QString& magnet_uri)
} }
QString misc::magnetUriToHash(const QString& magnet_uri) { QString misc::magnetUriToHash(const QString& magnet_uri) {
#if LIBTORRENT_VERSION_NUM < 1600
QString hash = ""; QString hash = "";
QRegExp regHex("urn:btih:([0-9A-Za-z]+)"); QRegExp regHex("urn:btih:([0-9A-Za-z]+)");
// Hex // Hex
@@ -397,6 +400,15 @@ QString misc::magnetUriToHash(const QString& magnet_uri) {
} }
qDebug("magnetUriToHash (base32): hash: %s", qPrintable(hash)); qDebug("magnetUriToHash (base32): hash: %s", qPrintable(hash));
return hash; return hash;
#else
add_torrent_params p;
error_code ec;
parse_magnet_uri(magnet_uri.toUtf8().constData(), p, ec);
if (ec)
return QString::null;
return toQString(p.info_hash);
#endif
} }
// Take a number of seconds and return an user-friendly // Take a number of seconds and return an user-friendly
@@ -556,15 +568,29 @@ QString misc::toQString(time_t t)
bool misc::naturalSort(QString left, QString right, bool &result) { // uses lessThan comparison bool misc::naturalSort(QString left, QString right, bool &result) { // uses lessThan comparison
// Return value indicates if functions was successful // Return value indicates if functions was successful
// result argument will contain actual comparison result if function was successful // result argument will contain actual comparison result if function was successful
int posL = 0;
int posR = 0;
do { do {
int posL = left.indexOf(QRegExp("[0-9]")); for (;;) {
int posR = right.indexOf(QRegExp("[0-9]")); if (posL == left.size() || posR == right.size())
if (posL == -1 || posR == -1) return false; // No data
break; // No data
else if (posL != posR) QChar leftChar = left.at(posL);
break; // Digit positions mismatch QChar rightChar = right.at(posR);
else if (left.left(posL) != right.left(posR)) bool leftCharIsDigit = leftChar.isDigit();
break; // Strings' subsets before digit do not match bool rightCharIsDigit = rightChar.isDigit();
if (leftCharIsDigit != rightCharIsDigit)
return false; // Digit positions mismatch
if (leftCharIsDigit)
break; // Both are digit, break this loop and compare numbers
if (leftChar != rightChar)
return false; // Strings' subsets before digit do not match
++posL;
++posR;
}
QString temp; QString temp;
while (posL < left.size()) { while (posL < left.size()) {
@@ -593,8 +619,6 @@ bool misc::naturalSort(QString left, QString right, bool &result) { // uses less
// Strings + digits do match and we haven't hit string end // Strings + digits do match and we haven't hit string end
// Do another round // Do another round
left.remove(0, posL);
right.remove(0, posR);
} while (true); } while (true);

View File

@@ -32,6 +32,12 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimumSize">
<size>
<width>116</width>
<height>0</height>
</size>
</property>
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::StyledPanel</enum> <enum>QFrame::StyledPanel</enum>
</property> </property>
@@ -78,9 +84,6 @@
<property name="text"> <property name="text">
<string>Behavior</string> <string>Behavior</string>
</property> </property>
<property name="toolTip">
<string>Behavior</string>
</property>
<property name="textAlignment"> <property name="textAlignment">
<set>AlignHCenter|AlignVCenter|AlignCenter</set> <set>AlignHCenter|AlignVCenter|AlignCenter</set>
</property> </property>

View File

@@ -310,8 +310,8 @@ void options_imp::loadWindowState() {
sizes << sizes_str.first().toInt(); sizes << sizes_str.first().toInt();
sizes << sizes_str.last().toInt(); sizes << sizes_str.last().toInt();
} else { } else {
sizes << 130; sizes << 116;
sizes << hsplitter->width()-130; sizes << hsplitter->width()-116;
} }
hsplitter->setSizes(sizes); hsplitter->setSizes(sizes);
} }

View File

@@ -1226,16 +1226,18 @@ public:
} }
#ifdef Q_WS_WIN #ifdef Q_WS_WIN
static QString getPythonPath() { static QString Preferences::getPythonPath()
{
QString path = pythonSearchReg(USER); QString path = pythonSearchReg(USER);
if (path.isEmpty()) if (!path.isEmpty())
return path;
path = pythonSearchReg(SYSTEM_32BIT); path = pythonSearchReg(SYSTEM_32BIT);
else return path; if (!path.isEmpty())
return path;
if (path.isEmpty())
path = pythonSearchReg(SYSTEM_64BIT); path = pythonSearchReg(SYSTEM_64BIT);
else return path;
if (!path.isEmpty()) if (!path.isEmpty())
return path; return path;
@@ -1243,10 +1245,11 @@ public:
QStringList supported_versions; QStringList supported_versions;
supported_versions << "32" << "31" << "30" << "27" << "26" << "25"; supported_versions << "32" << "31" << "30" << "27" << "26" << "25";
foreach (const QString &v, supported_versions) { foreach (const QString &v, supported_versions) {
if (QFile::exists("C:/Python"+v+"/python.exe")) if (QFile::exists("C:/Python" + v + "/python.exe"))
return "C:\\Python"+v; return "C:/Python" + v;
} }
return QString::null;
return QString();
} }
bool neverCheckFileAssoc() const { bool neverCheckFileAssoc() const {
@@ -1277,7 +1280,7 @@ public:
return false; return false;
QString assoc_exe = exe_reg.cap(1); QString assoc_exe = exe_reg.cap(1);
qDebug("exe: %s", qPrintable(assoc_exe)); qDebug("exe: %s", qPrintable(assoc_exe));
if (assoc_exe.compare(qApp->applicationFilePath(), Qt::CaseInsensitive) != 0) if (assoc_exe.compare(qApp->applicationFilePath().replace("/", "\\"), Qt::CaseInsensitive) != 0)
return false; return false;
return true; return true;
} }
@@ -1303,8 +1306,8 @@ public:
// Magnet association // Magnet association
if (set) { if (set) {
const QString command_str = "\""+qApp->applicationFilePath()+"\" \"%1\""; const QString command_str = "\""+qApp->applicationFilePath().replace("/", "\\")+"\" \"%1\"";
const QString icon_str = "\""+qApp->applicationFilePath()+"\",1"; const QString icon_str = "\""+qApp->applicationFilePath().replace("/", "\\")+"\",1";
settings.setValue("magnet/Default", "URL:Magnet link"); settings.setValue("magnet/Default", "URL:Magnet link");
settings.setValue("magnet/Content Type", "application/x-magnet"); settings.setValue("magnet/Content Type", "application/x-magnet");
@@ -1361,121 +1364,124 @@ public:
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
private: private:
enum REG_SEARCH_TYPE {USER, SYSTEM_32BIT, SYSTEM_64BIT}; enum REG_SEARCH_TYPE
{
USER,
SYSTEM_32BIT,
SYSTEM_64BIT
};
static QStringList getRegSubkeys(const HKEY &handle) { static QStringList getRegSubkeys(HKEY handle)
{
QStringList keys; QStringList keys;
DWORD subkeys_count = 0;
DWORD max_subkey_len = 0;
long res = ::RegQueryInfoKey(handle, NULL, NULL, NULL, &subkeys_count, &max_subkey_len, NULL, NULL, NULL, NULL, NULL, NULL);
if (res == ERROR_SUCCESS) {
max_subkey_len++; //For null character
LPTSTR key_name = new TCHAR[max_subkey_len];
for (uint i=0; i<subkeys_count; i++) { DWORD cSubKeys = 0;
res = ::RegEnumKeyEx(handle, 0, key_name, &max_subkey_len, NULL, NULL, NULL, NULL); DWORD cMaxSubKeyLen = 0;
LONG res = ::RegQueryInfoKeyW(handle, NULL, NULL, NULL, &cSubKeys, &cMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL);
if (res == ERROR_SUCCESS) {
cMaxSubKeyLen++; // For null character
LPWSTR lpName = new WCHAR[cMaxSubKeyLen];
DWORD cName;
for (DWORD i = 0; i < cSubKeys; ++i) {
cName = cMaxSubKeyLen;
res = ::RegEnumKeyExW(handle, 0, lpName, &cName, NULL, NULL, NULL, NULL);
if (res == ERROR_SUCCESS) if (res == ERROR_SUCCESS)
keys.push_back(QString::fromWCharArray(key_name)); keys.push_back(QString::fromWCharArray(lpName));
} }
delete[] key_name;
delete[] lpName;
} }
return keys; return keys;
} }
static QString getRegValue(const HKEY &handle, const QString &name = QString()) { static QString getRegValue(HKEY handle, const QString &name = QString())
QString end_result; {
DWORD type = 0; QString result;
DWORD size = 0;
DWORD array_size = 0;
LPTSTR value_name = NULL; DWORD type = 0;
DWORD cbData = 0;
LPWSTR lpValueName = NULL;
if (!name.isEmpty()) { if (!name.isEmpty()) {
value_name = new TCHAR[name.size()+1]; lpValueName = new WCHAR[name.size() + 1];
name.toWCharArray(value_name); name.toWCharArray(lpValueName);
value_name[name.size()] = '\0'; lpValueName[name.size()] = 0;
} }
// Discover the size of the value // Discover the size of the value
::RegQueryValueEx(handle, value_name, NULL, &type, NULL, &size); ::RegQueryValueExW(handle, lpValueName, NULL, &type, NULL, &cbData);
array_size = size / sizeof(TCHAR); DWORD cBuffer = (cbData / sizeof(WCHAR)) + 1;
if (size % sizeof(TCHAR)) LPWSTR lpData = new WCHAR[cBuffer];
array_size++; LONG res = ::RegQueryValueExW(handle, lpValueName, NULL, &type, (LPBYTE)lpData, &cbData);
array_size++; //For null character if (lpValueName)
LPTSTR value = new TCHAR[array_size]; delete[] lpValueName;
long res = ::RegQueryValueEx(handle, value_name, NULL, &type, (LPBYTE)value, &size);
if (res == ERROR_SUCCESS) {
value[array_size] = '\0';
end_result = QString::fromWCharArray(value);
}
if (value_name)
delete[] value_name;
if (value)
delete[] value;
return end_result;
}
static QString pythonSearchReg(const REG_SEARCH_TYPE type) {
HKEY key_handle1;
long res = 0;
switch (type) {
case USER:
res = ::RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Python\\PythonCore"), 0, KEY_READ, &key_handle1);
break;
case SYSTEM_32BIT:
res = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Python\\PythonCore"), 0, KEY_READ|KEY_WOW64_32KEY, &key_handle1);
break;
case SYSTEM_64BIT:
res = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Python\\PythonCore"), 0, KEY_READ|KEY_WOW64_64KEY, &key_handle1);
break;
}
if (res == ERROR_SUCCESS) { if (res == ERROR_SUCCESS) {
QStringList versions = getRegSubkeys(key_handle1); lpData[cBuffer - 1] = 0;
result = QString::fromWCharArray(lpData);
}
delete[] lpData;
return result;
}
static QString pythonSearchReg(const REG_SEARCH_TYPE type)
{
HKEY hkRoot;
if (type == USER)
hkRoot = HKEY_CURRENT_USER;
else
hkRoot = HKEY_LOCAL_MACHINE;
REGSAM samDesired = KEY_READ;
if (type == SYSTEM_32BIT)
samDesired |= KEY_WOW64_32KEY;
else if (type == SYSTEM_64BIT)
samDesired |= KEY_WOW64_64KEY;
QString path;
LONG res = 0;
HKEY hkPythonCore;
res = ::RegOpenKeyExW(hkRoot, L"SOFTWARE\\Python\\PythonCore", 0, samDesired, &hkPythonCore);
if (res == ERROR_SUCCESS) {
QStringList versions = getRegSubkeys(hkPythonCore);
qDebug("Python versions nb: %d", versions.size()); qDebug("Python versions nb: %d", versions.size());
versions.sort(); versions.sort();
while(!versions.empty()) { bool found = false;
const QString version = versions.takeLast()+"\\InstallPath"; while(!found && !versions.empty()) {
HKEY key_handle2; const QString version = versions.takeLast() + "\\InstallPath";
LPTSTR subkey = new TCHAR[version.size()+1]; LPWSTR lpSubkey = new WCHAR[version.size() + 1];
version.toWCharArray(subkey); version.toWCharArray(lpSubkey);
subkey[version.size()] = '\0'; lpSubkey[version.size()] = 0;
switch (type) { HKEY hkInstallPath;
case USER: res = ::RegOpenKeyExW(hkPythonCore, lpSubkey, 0, samDesired, &hkInstallPath);
res = ::RegOpenKeyEx(key_handle1, subkey, 0, KEY_READ, &key_handle2); delete[] lpSubkey;
break;
case SYSTEM_32BIT:
res = ::RegOpenKeyEx(key_handle1, subkey, 0, KEY_READ|KEY_WOW64_32KEY, &key_handle2);
break;
case SYSTEM_64BIT:
res = ::RegOpenKeyEx(key_handle1, subkey, 0, KEY_READ|KEY_WOW64_64KEY, &key_handle2);
break;
}
delete[] subkey;
if (res == ERROR_SUCCESS) { if (res == ERROR_SUCCESS) {
qDebug("Detected possible Python v%s location", qPrintable(version)); qDebug("Detected possible Python v%s location", qPrintable(version));
QString path = getRegValue(key_handle2); path = getRegValue(hkInstallPath);
::RegCloseKey(key_handle2); ::RegCloseKey(hkInstallPath);
if (!path.isEmpty() && QDir(path).exists("python.exe")) { if (!path.isEmpty() && QDir(path).exists("python.exe")) {
qDebug("Found python.exe at %s", qPrintable(path)); qDebug("Found python.exe at %s", qPrintable(path));
::RegCloseKey(key_handle1); found = true;
}
}
}
if (!found)
path = QString();
::RegCloseKey(hkPythonCore);
}
return path; return path;
} }
}
else
::RegCloseKey(key_handle2);
}
}
::RegCloseKey(key_handle1);
return QString::null;
}
#endif #endif
}; };

View File

@@ -40,10 +40,10 @@
#include "preferences.h" #include "preferences.h"
#ifdef Q_WS_MAC #ifdef Q_WS_MAC
const QUrl RSS_URL("http://sourceforge.net/api/file/index/project-id/163414/mtime/desc/rss?path=/qbittorrent-mac"); const QUrl RSS_URL("http://sourceforge.net/projects/qbittorrent/rss?path=/qbittorrent-mac");
const QString FILE_EXT = "DMG"; const QString FILE_EXT = "DMG";
#else #else
const QUrl RSS_URL("http://sourceforge.net/api/file/index/project-id/163414/mtime/desc/rss?path=/qbittorrent-win32"); const QUrl RSS_URL("http://sourceforge.net/projects/qbittorrent/rss?path=/qbittorrent-win32");
const QString FILE_EXT = "EXE"; const QString FILE_EXT = "EXE";
#endif #endif

View File

@@ -384,7 +384,7 @@ void PropertiesWidget::loadDynamicData() {
} }
} }
} catch(const invalid_handle& e) { } catch(const invalid_handle& e) {
qWarning() << "Caught exception in PropertiesWidget::loadDynamicData(): " << e.what(); qWarning() << "Caught exception in PropertiesWidget::loadDynamicData(): " << misc::toQStringU(e.what());
} }
} }
@@ -453,8 +453,10 @@ void PropertiesWidget::openFolder(const QModelIndex &index, bool containing_fold
void PropertiesWidget::displayFilesListMenu(const QPoint&) { void PropertiesWidget::displayFilesListMenu(const QPoint&) {
if (!h.is_valid()) if (!h.is_valid())
return; return;
QMenu myFilesLlistMenu;
QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0); QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0);
if (selectedRows.empty())
return;
QMenu myFilesLlistMenu;
QAction *actOpen = 0; QAction *actOpen = 0;
QAction *actOpenContainingFolder = 0; QAction *actOpenContainingFolder = 0;
QAction *actRename = 0; QAction *actRename = 0;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 172 KiB

View File

@@ -1110,7 +1110,7 @@ QTorrentHandle QBtSession::addTorrent(QString path, bool fromScanDir, QString fr
} catch(std::exception& e) { } catch(std::exception& e) {
if (!from_url.isNull()) { if (!from_url.isNull()) {
addConsoleMessage(tr("Unable to decode torrent file: '%1'", "e.g: Unable to decode torrent file: '/home/y/xxx.torrent'").arg(from_url), QString::fromUtf8("red")); addConsoleMessage(tr("Unable to decode torrent file: '%1'", "e.g: Unable to decode torrent file: '/home/y/xxx.torrent'").arg(from_url), QString::fromUtf8("red"));
addConsoleMessage(misc::toQString(e.what()), "red"); addConsoleMessage(misc::toQStringU(e.what()), "red");
//emit invalidTorrent(from_url); //emit invalidTorrent(from_url);
fsutils::forceRemove(path); fsutils::forceRemove(path);
}else{ }else{

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
#VERSION: 1.22 #VERSION: 1.23
#AUTHORS: BTDigg team (research@btdigg.org) #AUTHORS: BTDigg team (research@btdigg.org)
# #
# GNU GENERAL PUBLIC LICENSE # GNU GENERAL PUBLIC LICENSE
@@ -66,7 +66,7 @@ class btdigg(object):
pass pass
def search(self, what, cat='all'): def search(self, what, cat='all'):
req = what.replace('+', ' ') req = urllib.unquote(what)
u = urllib2.urlopen('https://api.btdigg.org/api/public-8e9a50f8335b964f/s01?%s' % (urllib.urlencode(dict(q = req)),)) u = urllib2.urlopen('https://api.btdigg.org/api/public-8e9a50f8335b964f/s01?%s' % (urllib.urlencode(dict(q = req)),))
try: try:

View File

@@ -1,6 +1,7 @@
#VERSION: 1.53 #VERSION: 2.00
#AUTHORS: Fabien Devaux (fab@gnux.info) #AUTHORS: Fabien Devaux (fab@gnux.info)
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org) #CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
# Arthur (custparasite@gmx.se)
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
@@ -27,93 +28,111 @@
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from novaprinter import prettyPrinter from novaprinter import prettyPrinter
import sgmllib from HTMLParser import HTMLParser
from helpers import retrieve_url, download_file from helpers import download_file
import urllib2
PREVIOUS_IDS = set() PREVIOUS_IDS = set()
class piratebay(object): class piratebay(object):
url = 'https://thepiratebay.se' url = 'http://thepiratebay.se'
name = 'The Pirate Bay' name = 'The Pirate Bay'
supported_categories = {'all': '0', 'movies': '200', 'music': '100', 'games': '400', 'software': '300'} supported_categories = {'all': '0', 'music': '100', 'movies': '200', 'games': '400', 'software': '300'}
def __init__(self):
self.results = []
self.parser = self.SimpleSGMLParser(self.results, self.url)
def download_torrent(self, info): def download_torrent(self, info):
print download_file(info) print(download_file(info))
class SimpleSGMLParser(sgmllib.SGMLParser): class MyHtmlParseWithBlackJack(HTMLParser):
def __init__(self, results, url, *args): def __init__(self, results, url):
sgmllib.SGMLParser.__init__(self) HTMLParser.__init__(self)
self.td_counter = None
self.current_item = None
self.results = results
self.url = url self.url = url
self.code = 0 self.results = results
self.in_name = None self.current_item = None
self.size_found = False
self.unit_found = False
self.seed_found = False
self.skip_td = False
self.leech_found = False
self.dispatcher = {'a' : self.handle_tag_a_ref,
'font' : self.handle_tag_font_size,
'td' : self.handle_tag_td_sl }
def start_a(self, attr): def handle_tag_a_ref(self, attrs):
params = dict(attr) params = dict(attrs)
#1
if params['href'].startswith('/torrent/'): if params['href'].startswith('/torrent/'):
get_id = params['href'].split('/')[2]
if not get_id in PREVIOUS_IDS:
self.current_item = {} self.current_item = {}
self.td_counter = 0
self.current_item['desc_link'] = self.url + params['href'].strip() self.current_item['desc_link'] = self.url + params['href'].strip()
self.in_name = True self.current_item['name'] = params['title'][12:].strip()
self.current_item['id'] = params['href'].split('/')[2] self.current_item['id'] = get_id
elif params['href'].startswith('magnet:'): #2
self.current_item['link']=params['href'].strip() elif (not self.current_item is None) and (params['href'].startswith('magnet:')):
self.in_name = False self.current_item['link'] = params['href'].strip()
def handle_tag_font_size(self, attrs):
if not self.current_item is None:
params = dict(attrs)
#3
if params['class'] == "detDesc":
self.size_found = True
def handle_tag_td_sl(self, attrs):
if not self.current_item is None:
params = dict(attrs)
if not self.current_item is None:
if self.seed_found:
#5
self.current_item['leech'] = ''
self.leech_found = True
self.seed_found = False
else:
#4
self.current_item['seeds'] = ''
self.seed_found = True
def handle_starttag(self, tag, attrs):
if tag in self.dispatcher:
self.dispatcher[tag](attrs)
def handle_data(self, data): def handle_data(self, data):
if self.td_counter == 0: if not self.current_item is None:
if self.in_name: if self.size_found:
if not self.current_item.has_key('name'): #with utf-8 you're going to have something like that: ['Uploaded', '10-02'], ['15:31,', 'Size', '240.34'], ['MiB,', 'ULed', 'by']
self.current_item['name'] = '' temp = data.split()
self.current_item['name']+= data.strip() if 'Size' in temp:
else: sizeIn = temp.index('Size')
#Parse size self.current_item['size'] = temp[sizeIn + 1]
if 'Size' in data: self.size_found = False
self.current_item['size'] = data[data.index("Size")+5:] self.unit_found = True
self.current_item['size'] = self.current_item['size'][:self.current_item['size'].index(',')] elif self.unit_found:
elif self.td_counter == 1: temp = data.split()
if not self.current_item.has_key('seeds'): self.current_item['size'] = ' '.join((self.current_item['size'], temp[0]))
self.current_item['seeds'] = '' self.unit_found = False
self.current_item['seeds']+= data.strip() elif self.seed_found:
elif self.td_counter == 2: self.current_item['seeds'] += data.rstrip()
if not self.current_item.has_key('leech'): elif self.leech_found:
self.current_item['leech'] = '' self.current_item['leech'] += data.rstrip()
self.current_item['leech']+= data.strip()
def start_td(self,attr):
if isinstance(self.td_counter,int):
self.td_counter += 1
if self.td_counter > 3:
self.td_counter = None
# Display item
if self.current_item:
if self.current_item['id'] in PREVIOUS_IDS:
self.results = []
self.reset()
return
self.current_item['engine_url'] = self.url self.current_item['engine_url'] = self.url
if not self.current_item['seeds'].isdigit():
self.current_item['seeds'] = 0
if not self.current_item['leech'].isdigit():
self.current_item['leech'] = 0
prettyPrinter(self.current_item) prettyPrinter(self.current_item)
PREVIOUS_IDS.add(self.current_item['id']) PREVIOUS_IDS.add(self.current_item['id'])
self.results.append('a') self.results.append('a')
self.current_item = None
self.size_found = False
self.unit_found = False
self.seed_found = False
self.leech_found = False
def search(self, what, cat='all'): def search(self, what, cat='all'):
ret = [] ret = []
i = 0 i = 0
order = 'se' while i < 11:
while True and i<11:
results = [] results = []
parser = self.SimpleSGMLParser(results, self.url) parser = self.MyHtmlParseWithBlackJack(results, self.url)
dat = retrieve_url(self.url+'/search/%s/%d/7/%s' % (what, i, self.supported_categories[cat])) query = '%s/search/%s/%d/99/%s' % (self.url, what, i, self.supported_categories[cat])
parser.feed(dat) dat = urllib2.urlopen(query)
parser.feed(dat.read().decode('utf-8'))
parser.close() parser.close()
if len(results) <= 0: if len(results) <= 0:
break break

View File

@@ -1,6 +1,7 @@
#VERSION: 1.32 #VERSION: 1.33
#AUTHORS: Gekko Dam Beer (gekko04@users.sourceforge.net) #AUTHORS: Gekko Dam Beer (gekko04@users.sourceforge.net)
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org) #CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
# Bruno Barbieri (brunorex@gmail.com)
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
@@ -27,8 +28,11 @@
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from novaprinter import prettyPrinter from novaprinter import prettyPrinter
import sgmllib
from helpers import retrieve_url, download_file from helpers import retrieve_url, download_file
from urllib2 import HTTPError
from HTMLParser import HTMLParser
import urllib
import re
class torrentreactor(object): class torrentreactor(object):
url = 'http://www.torrentreactor.net' url = 'http://www.torrentreactor.net'
@@ -38,29 +42,31 @@ class torrentreactor(object):
def download_torrent(self, info): def download_torrent(self, info):
print download_file(info) print download_file(info)
class SimpleSGMLParser(sgmllib.SGMLParser): class SimpleHTMLParser(HTMLParser):
def __init__(self, results, url, *args): def __init__(self, results, url, *args):
sgmllib.SGMLParser.__init__(self) HTMLParser.__init__(self)
self.td_counter = None self.td_counter = None
self.current_item = None self.current_item = None
self.results = results self.results = results
self.id = None self.id = None
self.url = url self.url = url
self.dispatcher = { 'a' : self.start_a, 'td' : self.start_td }
def handle_starttag(self, tag, attrs):
if tag in self.dispatcher:
self.dispatcher[tag](attrs)
def start_a(self, attr): def start_a(self, attr):
params = dict(attr) params = dict(attr)
if 'torrentreactor.net/download.php' in params['href']: if re.match("/torrents/\d+.*", params['href']):
self.current_item = {} self.current_item = {}
self.current_item['desc_link'] = self.url+params['href'].strip()
elif 'torrentreactor.net/download.php' in params['href']:
self.td_counter = 0 self.td_counter = 0
self.current_item['link'] = params['href'].strip() self.current_item['link'] = params['href'].strip()
elif params['href'].startswith('/torrents/'): self.current_item['name'] = urllib.unquote_plus(params['href'].split('&')[1].split('name=')[1])
self.current_item['desc_link'] = 'http://www.torrentreactor.net'+params['href'].strip()
def handle_data(self, data): def handle_data(self, data):
if self.td_counter == 0:
if not self.current_item.has_key('name'):
self.current_item['name'] = ''
self.current_item['name']+= data.strip()
if self.td_counter == 1: if self.td_counter == 1:
if not self.current_item.has_key('size'): if not self.current_item.has_key('size'):
self.current_item['size'] = '' self.current_item['size'] = ''
@@ -92,14 +98,20 @@ class torrentreactor(object):
def __init__(self): def __init__(self):
self.results = [] self.results = []
self.parser = self.SimpleSGMLParser(self.results, self.url) self.parser = self.SimpleHTMLParser(self.results, self.url)
def search(self, what, cat='all'): def search(self, what, cat='all'):
i = 0 i = 0
dat = ''
while True and i<11: while True and i<11:
results = [] results = []
parser = self.SimpleSGMLParser(results, self.url) parser = self.SimpleHTMLParser(results, self.url)
dat = retrieve_url(self.url+'/ts.php?search=&words=%s&cid=%s&sid=&type=1&orderby=a.seeds&asc=0&skip=%s'%(what, self.supported_categories[cat], (i*35)))
try:
dat = retrieve_url(self.url+'/torrent-search/%s/%s?sort=seeders.desc&type=all&period=none&categories=%s'%(what, (i*35), self.supported_categories[cat]))
except HTTPError:
break
parser.feed(dat) parser.feed(dat)
parser.close() parser.close()
if len(results) <= 0: if len(results) <= 0:

View File

@@ -1,8 +1,8 @@
torrentreactor: 1.32 torrentreactor: 1.33
mininova: 1.50 mininova: 1.50
piratebay: 1.53 piratebay: 2.00
vertor: 1.3 vertor: 1.3
extratorrent: 1.2 extratorrent: 1.2
kickasstorrents: 1.24 kickasstorrents: 1.24
btdigg: 1.22 btdigg: 1.23
legittorrents: 1.02 legittorrents: 1.02

View File

@@ -26,7 +26,7 @@
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
#VERSION: 1.31 #VERSION: 1.32
# Author: # Author:
# Fabien Devaux <fab AT gnux DOT info> # Fabien Devaux <fab AT gnux DOT info>
@@ -41,6 +41,7 @@ import sys
import threading import threading
import os import os
import glob import glob
import urllib
import fix_encoding import fix_encoding
@@ -138,7 +139,7 @@ if __name__ == '__main__':
if cat not in CATEGORIES: if cat not in CATEGORIES:
raise SystemExit('Invalid category!') raise SystemExit('Invalid category!')
what = '+'.join(sys.argv[3:]) what = urllib.quote(' '.join(sys.argv[3:]))
threads = [] threads = []
for engine in engines_list: for engine in engines_list:

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
#VERSION: 1.21 #VERSION: 1.23
#AUTHORS: BTDigg team (research@btdigg.org) #AUTHORS: BTDigg team (research@btdigg.org)
# #
# GNU GENERAL PUBLIC LICENSE # GNU GENERAL PUBLIC LICENSE
@@ -36,7 +36,7 @@ class btdigg(object):
pass pass
def search(self, what, cat='all'): def search(self, what, cat='all'):
req = urllib.parse.unquote(what).replace('+', ' ') req = urllib.parse.unquote(what)
u = urllib.request.urlopen('https://api.btdigg.org/api/public-8e9a50f8335b964f/s01?%s' % (urllib.parse.urlencode(dict(q = req)),)) u = urllib.request.urlopen('https://api.btdigg.org/api/public-8e9a50f8335b964f/s01?%s' % (urllib.parse.urlencode(dict(q = req)),))
try: try:

View File

@@ -1,6 +1,7 @@
#VERSION: 1.53 #VERSION: 2.00
#AUTHORS: Fabien Devaux (fab@gnux.info) #AUTHORS: Fabien Devaux (fab@gnux.info)
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org) #CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
# Arthur (custparasite@gmx.se)
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
@@ -27,93 +28,111 @@
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from novaprinter import prettyPrinter from novaprinter import prettyPrinter
import sgmllib3 from html.parser import HTMLParser
from helpers import retrieve_url, download_file from helpers import download_file
import urllib.request
PREVIOUS_IDS = set() PREVIOUS_IDS = set()
class piratebay(object): class piratebay(object):
url = 'https://thepiratebay.se' url = 'http://thepiratebay.se'
name = 'The Pirate Bay' name = 'The Pirate Bay'
supported_categories = {'all': '0', 'movies': '200', 'music': '100', 'games': '400', 'software': '300'} supported_categories = {'all': '0', 'music': '100', 'movies': '200', 'games': '400', 'software': '300'}
def __init__(self):
self.results = []
self.parser = self.SimpleSGMLParser(self.results, self.url)
def download_torrent(self, info): def download_torrent(self, info):
print(download_file(info)) print(download_file(info))
class SimpleSGMLParser(sgmllib3.SGMLParser): class MyHtmlParseWithBlackJack(HTMLParser):
def __init__(self, results, url, *args): def __init__(self, results, url):
sgmllib3.SGMLParser.__init__(self) super().__init__()
self.td_counter = None
self.current_item = None
self.results = results
self.url = url self.url = url
self.code = 0 self.results = results
self.in_name = None self.current_item = None
self.size_found = False
self.unit_found = False
self.seed_found = False
self.skip_td = False
self.leech_found = False
self.dispatcher = {'a' : self.handle_tag_a_ref,
'font' : self.handle_tag_font_size,
'td' : self.handle_tag_td_sl }
def start_a(self, attr): def handle_tag_a_ref(self, attrs):
params = dict(attr) params = dict(attrs)
#1
if params['href'].startswith('/torrent/'): if params['href'].startswith('/torrent/'):
get_id = params['href'].split('/')[2]
if not get_id in PREVIOUS_IDS:
self.current_item = {} self.current_item = {}
self.td_counter = 0
self.current_item['desc_link'] = self.url + params['href'].strip() self.current_item['desc_link'] = self.url + params['href'].strip()
self.in_name = True self.current_item['name'] = params['title'][12:].strip()
self.current_item['id'] = params['href'].split('/')[2] self.current_item['id'] = get_id
elif params['href'].startswith('magnet:'): #2
self.current_item['link']=params['href'].strip() elif (not self.current_item is None) and (params['href'].startswith('magnet:')):
self.in_name = False self.current_item['link'] = params['href'].strip()
def handle_tag_font_size(self, attrs):
if not self.current_item is None:
params = dict(attrs)
#3
if params['class'] == "detDesc":
self.size_found = True
def handle_tag_td_sl(self, attrs):
if not self.current_item is None:
params = dict(attrs)
if not self.current_item is None:
if self.seed_found:
#5
self.current_item['leech'] = ''
self.leech_found = True
self.seed_found = False
else:
#4
self.current_item['seeds'] = ''
self.seed_found = True
def handle_starttag(self, tag, attrs):
if tag in self.dispatcher:
self.dispatcher[tag](attrs)
def handle_data(self, data): def handle_data(self, data):
if self.td_counter == 0: if not self.current_item is None:
if self.in_name: if self.size_found:
if 'name' not in self.current_item: #with utf-8 you're going to have something like that: ['Uploaded', '10-02'], ['15:31,', 'Size', '240.34'], ['MiB,', 'ULed', 'by']
self.current_item['name'] = '' temp = data.split()
self.current_item['name']+= data.strip() if 'Size' in temp:
else: sizeIn = temp.index('Size')
#Parse size self.current_item['size'] = temp[sizeIn + 1]
if 'Size' in data: self.size_found = False
self.current_item['size'] = data[data.index("Size")+5:] self.unit_found = True
self.current_item['size'] = self.current_item['size'][:self.current_item['size'].index(',')] elif self.unit_found:
elif self.td_counter == 1: temp = data.split()
if 'seeds' not in self.current_item: self.current_item['size'] = ' '.join((self.current_item['size'], temp[0]))
self.current_item['seeds'] = '' self.unit_found = False
self.current_item['seeds']+= data.strip() elif self.seed_found:
elif self.td_counter == 2: self.current_item['seeds'] += data.rstrip()
if 'leech' not in self.current_item: elif self.leech_found:
self.current_item['leech'] = '' self.current_item['leech'] += data.rstrip()
self.current_item['leech']+= data.strip()
def start_td(self,attr):
if isinstance(self.td_counter,int):
self.td_counter += 1
if self.td_counter > 3:
self.td_counter = None
# Display item
if self.current_item:
if self.current_item['id'] in PREVIOUS_IDS:
self.results = []
self.reset()
return
self.current_item['engine_url'] = self.url self.current_item['engine_url'] = self.url
if not self.current_item['seeds'].isdigit():
self.current_item['seeds'] = 0
if not self.current_item['leech'].isdigit():
self.current_item['leech'] = 0
prettyPrinter(self.current_item) prettyPrinter(self.current_item)
PREVIOUS_IDS.add(self.current_item['id']) PREVIOUS_IDS.add(self.current_item['id'])
self.results.append('a') self.results.append('a')
self.current_item = None
self.size_found = False
self.unit_found = False
self.seed_found = False
self.leech_found = False
def search(self, what, cat='all'): def search(self, what, cat='all'):
ret = [] ret = []
i = 0 i = 0
order = 'se' while i < 11:
while True and i<11:
results = [] results = []
parser = self.SimpleSGMLParser(results, self.url) parser = self.MyHtmlParseWithBlackJack(results, self.url)
dat = retrieve_url(self.url+'/search/%s/%d/7/%s' % (what, i, self.supported_categories[cat])) query = '%s/search/%s/%d/99/%s' % (self.url, what, i, self.supported_categories[cat])
parser.feed(dat) dat = urllib.request.urlopen(query)
parser.feed(dat.read().decode('utf-8'))
parser.close() parser.close()
if len(results) <= 0: if len(results) <= 0:
break break

View File

@@ -1,6 +1,7 @@
#VERSION: 1.32 #VERSION: 1.33
#AUTHORS: Gekko Dam Beer (gekko04@users.sourceforge.net) #AUTHORS: Gekko Dam Beer (gekko04@users.sourceforge.net)
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org) #CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
# Bruno Barbieri (brunorex@gmail.com)
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
@@ -27,8 +28,10 @@
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
from novaprinter import prettyPrinter from novaprinter import prettyPrinter
import sgmllib3
from helpers import retrieve_url, download_file from helpers import retrieve_url, download_file
from urllib import error, parse
from html.parser import HTMLParser
import re
class torrentreactor(object): class torrentreactor(object):
url = 'http://www.torrentreactor.net' url = 'http://www.torrentreactor.net'
@@ -38,29 +41,31 @@ class torrentreactor(object):
def download_torrent(self, info): def download_torrent(self, info):
print(download_file(info)) print(download_file(info))
class SimpleSGMLParser(sgmllib3.SGMLParser): class SimpleHTMLParser(HTMLParser):
def __init__(self, results, url, *args): def __init__(self, results, url, *args):
sgmllib3.SGMLParser.__init__(self) HTMLParser.__init__(self)
self.td_counter = None self.td_counter = None
self.current_item = None self.current_item = None
self.results = results self.results = results
self.id = None self.id = None
self.url = url self.url = url
self.dispatcher = { 'a' : self.start_a, 'td' : self.start_td }
def handle_starttag(self, tag, attrs):
if tag in self.dispatcher:
self.dispatcher[tag](attrs)
def start_a(self, attr): def start_a(self, attr):
params = dict(attr) params = dict(attr)
if 'torrentreactor.net/download.php' in params['href']: if re.match("/torrents/\d+.*", params['href']):
self.current_item = {} self.current_item = {}
self.current_item['desc_link'] = self.url+params['href'].strip()
elif 'torrentreactor.net/download.php' in params['href']:
self.td_counter = 0 self.td_counter = 0
self.current_item['link'] = params['href'].strip() self.current_item['link'] = params['href'].strip()
elif params['href'].startswith('/torrents/'): self.current_item['name'] = parse.unquote_plus(params['href'].split('&')[1].split('name=')[1])
self.current_item['desc_link'] = 'http://www.torrentreactor.net'+params['href'].strip()
def handle_data(self, data): def handle_data(self, data):
if self.td_counter == 0:
if 'name' not in self.current_item:
self.current_item['name'] = ''
self.current_item['name']+= data.strip()
if self.td_counter == 1: if self.td_counter == 1:
if 'size' not in self.current_item: if 'size' not in self.current_item:
self.current_item['size'] = '' self.current_item['size'] = ''
@@ -92,14 +97,20 @@ class torrentreactor(object):
def __init__(self): def __init__(self):
self.results = [] self.results = []
self.parser = self.SimpleSGMLParser(self.results, self.url) self.parser = self.SimpleHTMLParser(self.results, self.url)
def search(self, what, cat='all'): def search(self, what, cat='all'):
i = 0 i = 0
dat = ''
while True and i<11: while True and i<11:
results = [] results = []
parser = self.SimpleSGMLParser(results, self.url) parser = self.SimpleHTMLParser(results, self.url)
dat = retrieve_url(self.url+'/ts.php?search=&words=%s&cid=%s&sid=&type=1&orderby=a.seeds&asc=0&skip=%s'%(what, self.supported_categories[cat], (i*35)))
try:
dat = retrieve_url(self.url+'/torrent-search/%s/%s?sort=seeders.desc&type=all&period=none&categories=%s'%(what, (i*35), self.supported_categories[cat]))
except error.HTTPError:
break
parser.feed(dat) parser.feed(dat)
parser.close() parser.close()
if len(results) <= 0: if len(results) <= 0:

View File

@@ -1,8 +1,8 @@
torrentreactor: 1.32 torrentreactor: 1.33
mininova: 1.50 mininova: 1.50
piratebay: 1.53 piratebay: 2.00
vertor: 1.3 vertor: 1.3
extratorrent: 1.2 extratorrent: 1.2
kickasstorrents: 1.24 kickasstorrents: 1.24
btdigg: 1.21 btdigg: 1.23
legittorrents: 1.02 legittorrents: 1.02

View File

@@ -26,7 +26,7 @@
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
#VERSION: 1.23 #VERSION: 1.24
# Author: # Author:
# Fabien Devaux <fab AT gnux DOT info> # Fabien Devaux <fab AT gnux DOT info>
@@ -134,7 +134,7 @@ if __name__ == '__main__':
if cat not in CATEGORIES: if cat not in CATEGORIES:
raise SystemExit('Invalid category!') raise SystemExit('Invalid category!')
what = urllib.parse.quote('+'.join(sys.argv[3:])) what = urllib.parse.quote(' '.join(sys.argv[3:]))
threads = [] threads = []
for engine in engines_list: for engine in engines_list:

View File

@@ -1,4 +1,4 @@
#VERSION: 1.43 #VERSION: 1.44
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met: # modification, are permitted provided that the following conditions are met:
@@ -24,22 +24,18 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE. # POSSIBILITY OF SUCH DAMAGE.
import sys
#import codecs
# Force UTF-8 printing
#sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
def prettyPrinter(dictionary): def prettyPrinter(dictionary):
# Convert everything to unicode for safe printing outtext = ''
#for key,value in list(dictionary.items()):
#if isinstance(dictionary[key], str):
# dictionary[key] = str(dictionary[key], 'utf-8')
dictionary['size'] = anySizeToBytes(dictionary['size']) dictionary['size'] = anySizeToBytes(dictionary['size'])
if 'desc_link' in dictionary: if 'desc_link' in dictionary:
print("%s|%s|%s|%s|%s|%s|%s"%(dictionary['link'],dictionary['name'].replace('|',' '),dictionary['size'],dictionary['seeds'],dictionary['leech'],dictionary['engine_url'],dictionary['desc_link'])) outtext = '%s|%s|%s|%s|%s|%s|%s'%(dictionary['link'],dictionary['name'].replace('|',' '),dictionary['size'],dictionary['seeds'],dictionary['leech'],dictionary['engine_url'],dictionary['desc_link'])
else: else:
print("%s|%s|%s|%s|%s|%s"%(dictionary['link'],dictionary['name'].replace('|',' '),dictionary['size'],dictionary['seeds'],dictionary['leech'],dictionary['engine_url'])) outtext = '%s|%s|%s|%s|%s|%s'%(dictionary['link'],dictionary['name'].replace('|',' '),dictionary['size'],dictionary['seeds'],dictionary['leech'],dictionary['engine_url'])
# fd 1 is stdout
with open(1, 'w', encoding='utf-8', closefd=False) as utf8stdout:
print(outtext, file=utf8stdout)
def anySizeToBytes(size_string): def anySizeToBytes(size_string):
""" """

View File

@@ -42,6 +42,7 @@
#include "torrentcreatorthread.h" #include "torrentcreatorthread.h"
#include "fs_utils.h" #include "fs_utils.h"
#include "misc.h"
#if LIBTORRENT_VERSION_NUM < 1600 #if LIBTORRENT_VERSION_NUM < 1600
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
@@ -130,7 +131,7 @@ void TorrentCreatorThread::run() {
if (abort) return; if (abort) return;
// calculate the hash for all pieces // calculate the hash for all pieces
const QString parent_path = fsutils::branchPath(input_path) + QDir::separator(); const QString parent_path = fsutils::branchPath(input_path) + QDir::separator();
set_piece_hashes(t, parent_path.toUtf8().constData(), boost::bind<void>(&sendProgressUpdateSignal, _1, t.num_pieces(), this)); set_piece_hashes(t, parent_path.toUtf8().constData(), boost::bind(sendProgressUpdateSignal, _1, t.num_pieces(), this));
// Set qBittorrent as creator and add user comment to // Set qBittorrent as creator and add user comment to
// torrent_info structure // torrent_info structure
t.set_creator(creator_str.toUtf8().constData()); t.set_creator(creator_str.toUtf8().constData());
@@ -156,6 +157,6 @@ void TorrentCreatorThread::run() {
emit updateProgress(100); emit updateProgress(100);
emit creationSuccess(save_path, parent_path); emit creationSuccess(save_path, parent_path);
} catch (std::exception& e) { } catch (std::exception& e) {
emit creationFailure(QString::fromLocal8Bit(e.what())); emit creationFailure(misc::toQStringU(e.what()));
} }
} }

View File

@@ -77,15 +77,15 @@ void TorrentImportDlg::on_browseContentBtn_clicked()
{ {
QIniSettings settings; QIniSettings settings;
const QString default_dir = settings.value(QString::fromUtf8("TorrentImport/LastContentDir"), QDir::homePath()).toString(); const QString default_dir = settings.value(QString::fromUtf8("TorrentImport/LastContentDir"), QDir::homePath()).toString();
// Test for multi-file taken from libtorrent/create_torrent.hpp -> create_torrent::create_torrent
bool multifile = t->num_files() > 1; bool multifile = t->num_files() > 1;
#if LIBTORRENT_VERSION_NUM >= 1600 #if LIBTORRENT_VERSION_NUM >= 1600
if (!multifile && has_parent_path(t->files().file_path(*(t->files().begin())))) if (!multifile && (QDir::fromNativeSeparators(misc::toQStringU(t->file_at(0).path)).indexOf('/') != -1))
multifile = true; multifile = true;
#else #else
if (!multifile && t->file_at(0).path.has_parent_path()) if (!multifile && t->file_at(0).path.has_parent_path())
multifile = true; multifile = true;
#endif #endif
if (!multifile) { if (!multifile) {
// Single file torrent // Single file torrent
#if LIBTORRENT_VERSION_NUM >= 1600 #if LIBTORRENT_VERSION_NUM >= 1600

View File

@@ -112,6 +112,7 @@ TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *main_window,
#if defined(Q_WS_MAC) #if defined(Q_WS_MAC)
setAttribute(Qt::WA_MacShowFocusRect, false); setAttribute(Qt::WA_MacShowFocusRect, false);
#endif #endif
header()->setStretchLastSection(false);
// Default hidden columns // Default hidden columns
if (!column_loaded) { if (!column_loaded) {

View File

@@ -257,7 +257,7 @@ QByteArray btjson::getTrackersForTorrent(const QString& hash)
tracker_list.append(tracker_dict); tracker_list.append(tracker_dict);
} }
} catch(const std::exception& e) { } catch(const std::exception& e) {
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << e.what(); qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
return QByteArray(); return QByteArray();
} }
@@ -318,7 +318,7 @@ QByteArray btjson::getPropertiesForTorrent(const QString& hash)
const qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); const qreal ratio = QBtSession::instance()->getRealRatio(h.hash());
data[KEY_PROP_RATIO] = ratio > 100. ? QString::fromUtf8("") : misc::accurateDoubleToString(ratio, 1, false); data[KEY_PROP_RATIO] = ratio > 100. ? QString::fromUtf8("") : misc::accurateDoubleToString(ratio, 1, false);
} catch(const std::exception& e) { } catch(const std::exception& e) {
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << e.what(); qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
return QByteArray(); return QByteArray();
} }
@@ -363,7 +363,7 @@ QByteArray btjson::getFilesForTorrent(const QString& hash)
file_list.append(file_dict); file_list.append(file_dict);
} }
} catch (const std::exception& e) { } catch (const std::exception& e) {
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << e.what(); qWarning() << Q_FUNC_INFO << "Invalid torrent: " << misc::toQStringU(e.what());
return QByteArray(); return QByteArray();
} }

View File

@@ -193,6 +193,7 @@ void HttpConnection::respond() {
if (nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) { if (nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) {
m_generator.setStatusLine(403, "Forbidden"); m_generator.setStatusLine(403, "Forbidden");
m_generator.setMessage(tr("Your IP address has been banned after too many failed authentication attempts.")); m_generator.setMessage(tr("Your IP address has been banned after too many failed authentication attempts."));
m_generator.setContentType("text/plain; charset=utf-8");
m_generator.setContentEncoding(m_parser.acceptsEncoding()); m_generator.setContentEncoding(m_parser.acceptsEncoding());
write(); write();
return; return;

View File

@@ -19,7 +19,7 @@ XPStyle on
!define CSIDL_APPDATA '0x1A' ;Application Data path !define CSIDL_APPDATA '0x1A' ;Application Data path
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path !define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
!define PROG_VERSION "3.1.10" !define PROG_VERSION "3.1.12"
!define MUI_FINISHPAGE_RUN !define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun !define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
!define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt) !define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt)
@@ -33,7 +33,7 @@ OutFile "qbittorrent_${PROG_VERSION}_setup.exe"
;Installer Version Information ;Installer Version Information
VIAddVersionKey "ProductName" "qBittorrent" VIAddVersionKey "ProductName" "qBittorrent"
VIAddVersionKey "CompanyName" "The qBittorrent project" VIAddVersionKey "CompanyName" "The qBittorrent project"
VIAddVersionKey "LegalCopyright" "Copyright ©2006-2014 The qBittorrent project" VIAddVersionKey "LegalCopyright" "Copyright ©2006-2015 The qBittorrent project"
VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client" VIAddVersionKey "FileDescription" "qBittorrent - A Bittorrent Client"
VIAddVersionKey "FileVersion" "${PROG_VERSION}" VIAddVersionKey "FileVersion" "${PROG_VERSION}"

View File

@@ -1,5 +1,5 @@
PROJECT_NAME = qbittorrent PROJECT_NAME = qbittorrent
PROJECT_VERSION = 3.1.10 PROJECT_VERSION = 3.1.12
os2 { os2 {
DEFINES += VERSION=\'\"v$${PROJECT_VERSION}\"\' DEFINES += VERSION=\'\"v$${PROJECT_VERSION}\"\'
@@ -9,4 +9,4 @@ os2 {
DEFINES += VERSION_MAJOR=3 DEFINES += VERSION_MAJOR=3
DEFINES += VERSION_MINOR=1 DEFINES += VERSION_MINOR=1
DEFINES += VERSION_BUGFIX=10 DEFINES += VERSION_BUGFIX=12