mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-21 16:07:23 -06:00
Compare commits
34 Commits
release-3.
...
v3_1_x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19adad5e43 | ||
|
|
edcd9539e7 | ||
|
|
066c70b047 | ||
|
|
2b24018f7a | ||
|
|
c421da873f | ||
|
|
1ae49d1743 | ||
|
|
aecb51c42b | ||
|
|
19c0a98701 | ||
|
|
ba1f9558a9 | ||
|
|
79f3c6439c | ||
|
|
99a596ab8d | ||
|
|
9a74b27a85 | ||
|
|
b12f250642 | ||
|
|
2dede108e7 | ||
|
|
7cf1e7b8ca | ||
|
|
e1934e8c16 | ||
|
|
14b958216b | ||
|
|
1120c14890 | ||
|
|
88075d9226 | ||
|
|
36464fcd59 | ||
|
|
f7f1c81238 | ||
|
|
b8da4bcf74 | ||
|
|
bf7a6aceb0 | ||
|
|
3ef2da898b | ||
|
|
d0cd939143 | ||
|
|
e36d76d457 | ||
|
|
daa4314093 | ||
|
|
f707d6c9d5 | ||
|
|
83b6619b16 | ||
|
|
8b322648c8 | ||
|
|
d159117965 | ||
|
|
1fd2dce0bd | ||
|
|
f97238e1c9 | ||
|
|
67355810ae |
27
Changelog
27
Changelog
@@ -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
|
||||
- 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)
|
||||
|
||||
11
macxconf.pri
11
macxconf.pri
@@ -7,12 +7,10 @@ CONFIG += link_pkgconfig
|
||||
PKGCONFIG += libtorrent-rasterbar
|
||||
DEFINES += BOOST_ASIO_DYN_LINK
|
||||
|
||||
# Special include/libs paths (macports)
|
||||
INCLUDEPATH += /usr/include/openssl /usr/include /opt/local/include/boost /opt/local/include
|
||||
LIBS += -L/opt/local/lib
|
||||
# Special include/libs paths (homebrew and macports)
|
||||
INCLUDEPATH += /usr/local/include /opt/local/include /usr/include
|
||||
LIBS += -L/usr/local/lib -L/opt/local/lib -L/usr/lib
|
||||
|
||||
# OpenSSL lib
|
||||
LIBS += -lssl -lcrypto
|
||||
# Boost system lib
|
||||
LIBS += -lboost_system-mt
|
||||
# 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.files = mac/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/qt_bg.qm \
|
||||
qt-translations/qt_ca.qm \
|
||||
|
||||
@@ -197,7 +197,7 @@ bool AddNewTorrentDialog::loadTorrent(const QString& torrent_path, const QString
|
||||
m_torrentInfo = new torrent_info(m_filePath.toUtf8().data());
|
||||
m_hash = misc::toQString(m_torrentInfo->info_hash());
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ QIcon IconProvider::generateDifferentSizes(const QIcon& icon)
|
||||
{
|
||||
QIcon new_icon;
|
||||
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;
|
||||
modes << QIcon::Normal << QIcon::Active << QIcon::Selected << QIcon::Disabled;
|
||||
foreach (const QSize& size, required_sizes) {
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.1.10</string>
|
||||
<string>3.1.12</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>qBit</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
@@ -59,7 +59,7 @@
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<string>YES</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2006-2014 The qBittorrent project</string>
|
||||
<string>Copyright © 2006-2015 The qBittorrent project</string>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
[Paths]
|
||||
Prefix = MacOS
|
||||
Translations = translations
|
||||
Plugins = PlugIns
|
||||
|
||||
@@ -228,9 +228,14 @@ int main(int argc, char *argv[]) {
|
||||
QStringList torrentCmdLine = app.arguments();
|
||||
//Pass program parameters if any
|
||||
QString message;
|
||||
QFileInfo torrentPath;
|
||||
for (int a = 1; a < torrentCmdLine.size(); ++a) {
|
||||
if (torrentCmdLine[a].startsWith("--")) continue;
|
||||
message += torrentCmdLine[a];
|
||||
torrentPath.setFile(torrentCmdLine[a]);
|
||||
if (torrentPath.exists())
|
||||
message += torrentPath.absoluteFilePath();
|
||||
else
|
||||
message += torrentCmdLine[a];
|
||||
if (a < argc-1)
|
||||
message += "|";
|
||||
}
|
||||
|
||||
10
src/mainwindow.cpp
Normal file → Executable file
10
src/mainwindow.cpp
Normal file → Executable file
@@ -101,7 +101,7 @@ using namespace libtorrent;
|
||||
*****************************************************/
|
||||
|
||||
// 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
|
||||
, has_python(false)
|
||||
#endif
|
||||
@@ -697,8 +697,14 @@ void MainWindow::setTabText(int index, QString text) const {
|
||||
}
|
||||
|
||||
bool MainWindow::unlockUI() {
|
||||
if (unlockDlgShowing)
|
||||
return false;
|
||||
else
|
||||
unlockDlgShowing = true;
|
||||
|
||||
bool ok = false;
|
||||
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;
|
||||
Preferences pref;
|
||||
QString real_pass_md5 = pref.getUILockPasswordMD5();
|
||||
@@ -1343,7 +1349,7 @@ void MainWindow::on_actionSearch_engine_triggered() {
|
||||
bool res = false;
|
||||
|
||||
// Check if python is already in PATH
|
||||
if (misc::pythonVersion())
|
||||
if (misc::pythonVersion() > 0)
|
||||
res = true;
|
||||
else
|
||||
res = addPythonPathToEnv();
|
||||
|
||||
@@ -155,6 +155,8 @@ private:
|
||||
#ifdef Q_OS_WIN
|
||||
bool addPythonPathToEnv();
|
||||
void installPython();
|
||||
|
||||
private slots:
|
||||
void pythonDownloadSuccess(QString url, QString file_path);
|
||||
void pythonDownloadFailure(QString url, QString error);
|
||||
#endif
|
||||
@@ -183,6 +185,7 @@ private:
|
||||
bool displaySpeedInTitle;
|
||||
bool force_exit;
|
||||
bool ui_locked;
|
||||
bool unlockDlgShowing;
|
||||
LineEdit *search_filter;
|
||||
// Keyboard shortcuts
|
||||
QShortcut *switchSearchShortcut;
|
||||
|
||||
46
src/misc.cpp
46
src/misc.cpp
@@ -70,6 +70,8 @@ const int UNLEN = 256;
|
||||
#endif
|
||||
#endif // DISABLE_GUI
|
||||
|
||||
#include <libtorrent/magnet_uri.hpp>
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
static struct { const char *source; const char *comment; } units[] = {
|
||||
@@ -136,8 +138,8 @@ void misc::shutdownComputer(shutDownAction action) {
|
||||
}
|
||||
#endif
|
||||
#ifdef Q_WS_MAC
|
||||
AEEventID EventToSend;
|
||||
if (action != SHUTDOWN_COMPUTER)
|
||||
if (sleep)
|
||||
EventToSend = kAESleep;
|
||||
else
|
||||
EventToSend = kAEShutDown;
|
||||
@@ -372,6 +374,7 @@ QList<QUrl> misc::magnetUriToTrackers(const QString& magnet_uri)
|
||||
}
|
||||
|
||||
QString misc::magnetUriToHash(const QString& magnet_uri) {
|
||||
#if LIBTORRENT_VERSION_NUM < 1600
|
||||
QString hash = "";
|
||||
QRegExp regHex("urn:btih:([0-9A-Za-z]+)");
|
||||
// Hex
|
||||
@@ -397,6 +400,15 @@ QString misc::magnetUriToHash(const QString& magnet_uri) {
|
||||
}
|
||||
qDebug("magnetUriToHash (base32): hash: %s", qPrintable(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
|
||||
@@ -556,15 +568,29 @@ QString misc::toQString(time_t t)
|
||||
bool misc::naturalSort(QString left, QString right, bool &result) { // uses lessThan comparison
|
||||
// Return value indicates if functions was successful
|
||||
// result argument will contain actual comparison result if function was successful
|
||||
int posL = 0;
|
||||
int posR = 0;
|
||||
do {
|
||||
int posL = left.indexOf(QRegExp("[0-9]"));
|
||||
int posR = right.indexOf(QRegExp("[0-9]"));
|
||||
if (posL == -1 || posR == -1)
|
||||
break; // No data
|
||||
else if (posL != posR)
|
||||
break; // Digit positions mismatch
|
||||
else if (left.left(posL) != right.left(posR))
|
||||
break; // Strings' subsets before digit do not match
|
||||
for (;;) {
|
||||
if (posL == left.size() || posR == right.size())
|
||||
return false; // No data
|
||||
|
||||
QChar leftChar = left.at(posL);
|
||||
QChar rightChar = right.at(posR);
|
||||
bool leftCharIsDigit = leftChar.isDigit();
|
||||
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;
|
||||
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
|
||||
// Do another round
|
||||
left.remove(0, posL);
|
||||
right.remove(0, posR);
|
||||
|
||||
} while (true);
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>116</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
@@ -78,9 +84,6 @@
|
||||
<property name="text">
|
||||
<string>Behavior</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Behavior</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignHCenter|AlignVCenter|AlignCenter</set>
|
||||
</property>
|
||||
|
||||
@@ -310,8 +310,8 @@ void options_imp::loadWindowState() {
|
||||
sizes << sizes_str.first().toInt();
|
||||
sizes << sizes_str.last().toInt();
|
||||
} else {
|
||||
sizes << 130;
|
||||
sizes << hsplitter->width()-130;
|
||||
sizes << 116;
|
||||
sizes << hsplitter->width()-116;
|
||||
}
|
||||
hsplitter->setSizes(sizes);
|
||||
}
|
||||
|
||||
@@ -1226,16 +1226,18 @@ public:
|
||||
}
|
||||
|
||||
#ifdef Q_WS_WIN
|
||||
static QString getPythonPath() {
|
||||
static QString Preferences::getPythonPath()
|
||||
{
|
||||
QString path = pythonSearchReg(USER);
|
||||
if (path.isEmpty())
|
||||
path = pythonSearchReg(SYSTEM_32BIT);
|
||||
else return path;
|
||||
if (!path.isEmpty())
|
||||
return path;
|
||||
|
||||
if (path.isEmpty())
|
||||
path = pythonSearchReg(SYSTEM_64BIT);
|
||||
else return path;
|
||||
path = pythonSearchReg(SYSTEM_32BIT);
|
||||
if (!path.isEmpty())
|
||||
return path;
|
||||
|
||||
|
||||
path = pythonSearchReg(SYSTEM_64BIT);
|
||||
if (!path.isEmpty())
|
||||
return path;
|
||||
|
||||
@@ -1243,10 +1245,11 @@ public:
|
||||
QStringList supported_versions;
|
||||
supported_versions << "32" << "31" << "30" << "27" << "26" << "25";
|
||||
foreach (const QString &v, supported_versions) {
|
||||
if (QFile::exists("C:/Python"+v+"/python.exe"))
|
||||
return "C:\\Python"+v;
|
||||
if (QFile::exists("C:/Python" + v + "/python.exe"))
|
||||
return "C:/Python" + v;
|
||||
}
|
||||
return QString::null;
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool neverCheckFileAssoc() const {
|
||||
@@ -1277,7 +1280,7 @@ public:
|
||||
return false;
|
||||
QString assoc_exe = exe_reg.cap(1);
|
||||
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 true;
|
||||
}
|
||||
@@ -1303,8 +1306,8 @@ public:
|
||||
|
||||
// Magnet association
|
||||
if (set) {
|
||||
const QString command_str = "\""+qApp->applicationFilePath()+"\" \"%1\"";
|
||||
const QString icon_str = "\""+qApp->applicationFilePath()+"\",1";
|
||||
const QString command_str = "\""+qApp->applicationFilePath().replace("/", "\\")+"\" \"%1\"";
|
||||
const QString icon_str = "\""+qApp->applicationFilePath().replace("/", "\\")+"\",1";
|
||||
|
||||
settings.setValue("magnet/Default", "URL:Magnet link");
|
||||
settings.setValue("magnet/Content Type", "application/x-magnet");
|
||||
@@ -1361,120 +1364,123 @@ public:
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
private:
|
||||
enum REG_SEARCH_TYPE {USER, SYSTEM_32BIT, SYSTEM_64BIT};
|
||||
|
||||
static QStringList getRegSubkeys(const HKEY &handle) {
|
||||
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];
|
||||
enum REG_SEARCH_TYPE
|
||||
{
|
||||
USER,
|
||||
SYSTEM_32BIT,
|
||||
SYSTEM_64BIT
|
||||
};
|
||||
|
||||
for (uint i=0; i<subkeys_count; i++) {
|
||||
res = ::RegEnumKeyEx(handle, 0, key_name, &max_subkey_len, NULL, NULL, NULL, NULL);
|
||||
static QStringList getRegSubkeys(HKEY handle)
|
||||
{
|
||||
QStringList keys;
|
||||
|
||||
DWORD cSubKeys = 0;
|
||||
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)
|
||||
keys.push_back(QString::fromWCharArray(key_name));
|
||||
keys.push_back(QString::fromWCharArray(lpName));
|
||||
}
|
||||
delete[] key_name;
|
||||
|
||||
delete[] lpName;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
static QString getRegValue(const HKEY &handle, const QString &name = QString()) {
|
||||
QString end_result;
|
||||
DWORD type = 0;
|
||||
DWORD size = 0;
|
||||
DWORD array_size = 0;
|
||||
static QString getRegValue(HKEY handle, const QString &name = QString())
|
||||
{
|
||||
QString result;
|
||||
|
||||
LPTSTR value_name = NULL;
|
||||
DWORD type = 0;
|
||||
DWORD cbData = 0;
|
||||
LPWSTR lpValueName = NULL;
|
||||
if (!name.isEmpty()) {
|
||||
value_name = new TCHAR[name.size()+1];
|
||||
name.toWCharArray(value_name);
|
||||
value_name[name.size()] = '\0';
|
||||
lpValueName = new WCHAR[name.size() + 1];
|
||||
name.toWCharArray(lpValueName);
|
||||
lpValueName[name.size()] = 0;
|
||||
}
|
||||
|
||||
// Discover the size of the value
|
||||
::RegQueryValueEx(handle, value_name, NULL, &type, NULL, &size);
|
||||
array_size = size / sizeof(TCHAR);
|
||||
if (size % sizeof(TCHAR))
|
||||
array_size++;
|
||||
array_size++; //For null character
|
||||
LPTSTR value = new TCHAR[array_size];
|
||||
::RegQueryValueExW(handle, lpValueName, NULL, &type, NULL, &cbData);
|
||||
DWORD cBuffer = (cbData / sizeof(WCHAR)) + 1;
|
||||
LPWSTR lpData = new WCHAR[cBuffer];
|
||||
LONG res = ::RegQueryValueExW(handle, lpValueName, NULL, &type, (LPBYTE)lpData, &cbData);
|
||||
if (lpValueName)
|
||||
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);
|
||||
lpData[cBuffer - 1] = 0;
|
||||
result = QString::fromWCharArray(lpData);
|
||||
}
|
||||
delete[] lpData;
|
||||
|
||||
if (value_name)
|
||||
delete[] value_name;
|
||||
if (value)
|
||||
delete[] value;
|
||||
|
||||
return end_result;
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString pythonSearchReg(const REG_SEARCH_TYPE type) {
|
||||
HKEY key_handle1;
|
||||
long res = 0;
|
||||
static QString pythonSearchReg(const REG_SEARCH_TYPE type)
|
||||
{
|
||||
HKEY hkRoot;
|
||||
if (type == USER)
|
||||
hkRoot = HKEY_CURRENT_USER;
|
||||
else
|
||||
hkRoot = HKEY_LOCAL_MACHINE;
|
||||
|
||||
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;
|
||||
}
|
||||
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(key_handle1);
|
||||
QStringList versions = getRegSubkeys(hkPythonCore);
|
||||
qDebug("Python versions nb: %d", versions.size());
|
||||
versions.sort();
|
||||
|
||||
while(!versions.empty()) {
|
||||
const QString version = versions.takeLast()+"\\InstallPath";
|
||||
HKEY key_handle2;
|
||||
LPTSTR subkey = new TCHAR[version.size()+1];
|
||||
version.toWCharArray(subkey);
|
||||
subkey[version.size()] = '\0';
|
||||
bool found = false;
|
||||
while(!found && !versions.empty()) {
|
||||
const QString version = versions.takeLast() + "\\InstallPath";
|
||||
LPWSTR lpSubkey = new WCHAR[version.size() + 1];
|
||||
version.toWCharArray(lpSubkey);
|
||||
lpSubkey[version.size()] = 0;
|
||||
|
||||
switch (type) {
|
||||
case USER:
|
||||
res = ::RegOpenKeyEx(key_handle1, subkey, 0, KEY_READ, &key_handle2);
|
||||
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;
|
||||
}
|
||||
HKEY hkInstallPath;
|
||||
res = ::RegOpenKeyExW(hkPythonCore, lpSubkey, 0, samDesired, &hkInstallPath);
|
||||
delete[] lpSubkey;
|
||||
|
||||
delete[] subkey;
|
||||
if (res == ERROR_SUCCESS) {
|
||||
qDebug("Detected possible Python v%s location", qPrintable(version));
|
||||
QString path = getRegValue(key_handle2);
|
||||
::RegCloseKey(key_handle2);
|
||||
path = getRegValue(hkInstallPath);
|
||||
::RegCloseKey(hkInstallPath);
|
||||
|
||||
if (!path.isEmpty() && QDir(path).exists("python.exe")) {
|
||||
qDebug("Found python.exe at %s", qPrintable(path));
|
||||
::RegCloseKey(key_handle1);
|
||||
return path;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
::RegCloseKey(key_handle2);
|
||||
}
|
||||
|
||||
if (!found)
|
||||
path = QString();
|
||||
|
||||
::RegCloseKey(hkPythonCore);
|
||||
}
|
||||
::RegCloseKey(key_handle1);
|
||||
return QString::null;
|
||||
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -40,10 +40,10 @@
|
||||
#include "preferences.h"
|
||||
|
||||
#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";
|
||||
#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";
|
||||
#endif
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ void PropertiesWidget::loadDynamicData() {
|
||||
}
|
||||
}
|
||||
} 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&) {
|
||||
if (!h.is_valid())
|
||||
return;
|
||||
QMenu myFilesLlistMenu;
|
||||
QModelIndexList selectedRows = filesList->selectionModel()->selectedRows(0);
|
||||
if (selectedRows.empty())
|
||||
return;
|
||||
QMenu myFilesLlistMenu;
|
||||
QAction *actOpen = 0;
|
||||
QAction *actOpenContainingFolder = 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 |
@@ -1110,7 +1110,7 @@ QTorrentHandle QBtSession::addTorrent(QString path, bool fromScanDir, QString fr
|
||||
} catch(std::exception& e) {
|
||||
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(misc::toQString(e.what()), "red");
|
||||
addConsoleMessage(misc::toQStringU(e.what()), "red");
|
||||
//emit invalidTorrent(from_url);
|
||||
fsutils::forceRemove(path);
|
||||
}else{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
#VERSION: 1.22
|
||||
#VERSION: 1.23
|
||||
#AUTHORS: BTDigg team (research@btdigg.org)
|
||||
#
|
||||
# GNU GENERAL PUBLIC LICENSE
|
||||
@@ -66,7 +66,7 @@ class btdigg(object):
|
||||
pass
|
||||
|
||||
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)),))
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#VERSION: 1.53
|
||||
#VERSION: 2.00
|
||||
#AUTHORS: Fabien Devaux (fab@gnux.info)
|
||||
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
# Arthur (custparasite@gmx.se)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
@@ -27,94 +28,112 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
import sgmllib
|
||||
from helpers import retrieve_url, download_file
|
||||
from HTMLParser import HTMLParser
|
||||
from helpers import download_file
|
||||
import urllib2
|
||||
|
||||
PREVIOUS_IDS = set()
|
||||
|
||||
class piratebay(object):
|
||||
url = 'https://thepiratebay.se'
|
||||
name = 'The Pirate Bay'
|
||||
supported_categories = {'all': '0', 'movies': '200', 'music': '100', 'games': '400', 'software': '300'}
|
||||
url = 'http://thepiratebay.se'
|
||||
name = 'The Pirate Bay'
|
||||
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):
|
||||
print(download_file(info))
|
||||
|
||||
def download_torrent(self, info):
|
||||
print download_file(info)
|
||||
class MyHtmlParseWithBlackJack(HTMLParser):
|
||||
def __init__(self, results, url):
|
||||
HTMLParser.__init__(self)
|
||||
self.url = url
|
||||
self.results = results
|
||||
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 }
|
||||
|
||||
class SimpleSGMLParser(sgmllib.SGMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib.SGMLParser.__init__(self)
|
||||
self.td_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
self.url = url
|
||||
self.code = 0
|
||||
self.in_name = None
|
||||
def handle_tag_a_ref(self, attrs):
|
||||
params = dict(attrs)
|
||||
#1
|
||||
if params['href'].startswith('/torrent/'):
|
||||
get_id = params['href'].split('/')[2]
|
||||
if not get_id in PREVIOUS_IDS:
|
||||
self.current_item = {}
|
||||
self.current_item['desc_link'] = self.url + params['href'].strip()
|
||||
self.current_item['name'] = params['title'][12:].strip()
|
||||
self.current_item['id'] = get_id
|
||||
#2
|
||||
elif (not self.current_item is None) and (params['href'].startswith('magnet:')):
|
||||
self.current_item['link'] = params['href'].strip()
|
||||
|
||||
def start_a(self, attr):
|
||||
params = dict(attr)
|
||||
if params['href'].startswith('/torrent/'):
|
||||
self.current_item = {}
|
||||
self.td_counter = 0
|
||||
self.current_item['desc_link'] = self.url + params['href'].strip()
|
||||
self.in_name = True
|
||||
self.current_item['id'] = params['href'].split('/')[2]
|
||||
elif params['href'].startswith('magnet:'):
|
||||
self.current_item['link']=params['href'].strip()
|
||||
self.in_name = False
|
||||
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_data(self, data):
|
||||
if self.td_counter == 0:
|
||||
if self.in_name:
|
||||
if not self.current_item.has_key('name'):
|
||||
self.current_item['name'] = ''
|
||||
self.current_item['name']+= data.strip()
|
||||
else:
|
||||
#Parse size
|
||||
if 'Size' in data:
|
||||
self.current_item['size'] = data[data.index("Size")+5:]
|
||||
self.current_item['size'] = self.current_item['size'][:self.current_item['size'].index(',')]
|
||||
elif self.td_counter == 1:
|
||||
if not self.current_item.has_key('seeds'):
|
||||
self.current_item['seeds'] = ''
|
||||
self.current_item['seeds']+= data.strip()
|
||||
elif self.td_counter == 2:
|
||||
if not self.current_item.has_key('leech'):
|
||||
self.current_item['leech'] = ''
|
||||
self.current_item['leech']+= data.strip()
|
||||
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 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
|
||||
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)
|
||||
PREVIOUS_IDS.add(self.current_item['id'])
|
||||
self.results.append('a')
|
||||
def search(self, what, cat='all'):
|
||||
ret = []
|
||||
i = 0
|
||||
order = 'se'
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url)
|
||||
dat = retrieve_url(self.url+'/search/%s/%d/7/%s' % (what, i, self.supported_categories[cat]))
|
||||
parser.feed(dat)
|
||||
parser.close()
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag in self.dispatcher:
|
||||
self.dispatcher[tag](attrs)
|
||||
|
||||
def handle_data(self, data):
|
||||
if not self.current_item is None:
|
||||
if self.size_found:
|
||||
#with utf-8 you're going to have something like that: ['Uploaded', '10-02'], ['15:31,', 'Size', '240.34'], ['MiB,', 'ULed', 'by']
|
||||
temp = data.split()
|
||||
if 'Size' in temp:
|
||||
sizeIn = temp.index('Size')
|
||||
self.current_item['size'] = temp[sizeIn + 1]
|
||||
self.size_found = False
|
||||
self.unit_found = True
|
||||
elif self.unit_found:
|
||||
temp = data.split()
|
||||
self.current_item['size'] = ' '.join((self.current_item['size'], temp[0]))
|
||||
self.unit_found = False
|
||||
elif self.seed_found:
|
||||
self.current_item['seeds'] += data.rstrip()
|
||||
elif self.leech_found:
|
||||
self.current_item['leech'] += data.rstrip()
|
||||
self.current_item['engine_url'] = self.url
|
||||
prettyPrinter(self.current_item)
|
||||
PREVIOUS_IDS.add(self.current_item['id'])
|
||||
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'):
|
||||
ret = []
|
||||
i = 0
|
||||
while i < 11:
|
||||
results = []
|
||||
parser = self.MyHtmlParseWithBlackJack(results, self.url)
|
||||
query = '%s/search/%s/%d/99/%s' % (self.url, what, i, self.supported_categories[cat])
|
||||
dat = urllib2.urlopen(query)
|
||||
parser.feed(dat.read().decode('utf-8'))
|
||||
parser.close()
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#VERSION: 1.32
|
||||
#VERSION: 1.33
|
||||
#AUTHORS: Gekko Dam Beer (gekko04@users.sourceforge.net)
|
||||
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
# Bruno Barbieri (brunorex@gmail.com)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
@@ -27,8 +28,11 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
import sgmllib
|
||||
from helpers import retrieve_url, download_file
|
||||
from urllib2 import HTTPError
|
||||
from HTMLParser import HTMLParser
|
||||
import urllib
|
||||
import re
|
||||
|
||||
class torrentreactor(object):
|
||||
url = 'http://www.torrentreactor.net'
|
||||
@@ -37,30 +41,32 @@ class torrentreactor(object):
|
||||
|
||||
def download_torrent(self, info):
|
||||
print download_file(info)
|
||||
|
||||
class SimpleSGMLParser(sgmllib.SGMLParser):
|
||||
|
||||
class SimpleHTMLParser(HTMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib.SGMLParser.__init__(self)
|
||||
HTMLParser.__init__(self)
|
||||
self.td_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
self.id = None
|
||||
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):
|
||||
params = dict(attr)
|
||||
if 'torrentreactor.net/download.php' in params['href']:
|
||||
if re.match("/torrents/\d+.*", params['href']):
|
||||
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.current_item['link'] = params['href'].strip()
|
||||
elif params['href'].startswith('/torrents/'):
|
||||
self.current_item['desc_link'] = 'http://www.torrentreactor.net'+params['href'].strip()
|
||||
self.current_item['name'] = urllib.unquote_plus(params['href'].split('&')[1].split('name=')[1])
|
||||
|
||||
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 not self.current_item.has_key('size'):
|
||||
self.current_item['size'] = ''
|
||||
@@ -92,14 +98,20 @@ class torrentreactor(object):
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
self.parser = self.SimpleSGMLParser(self.results, self.url)
|
||||
self.parser = self.SimpleHTMLParser(self.results, self.url)
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
i = 0
|
||||
dat = ''
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(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)))
|
||||
parser = self.SimpleHTMLParser(results, self.url)
|
||||
|
||||
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.close()
|
||||
if len(results) <= 0:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
torrentreactor: 1.32
|
||||
torrentreactor: 1.33
|
||||
mininova: 1.50
|
||||
piratebay: 1.53
|
||||
piratebay: 2.00
|
||||
vertor: 1.3
|
||||
extratorrent: 1.2
|
||||
kickasstorrents: 1.24
|
||||
btdigg: 1.22
|
||||
btdigg: 1.23
|
||||
legittorrents: 1.02
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#VERSION: 1.31
|
||||
#VERSION: 1.32
|
||||
|
||||
# Author:
|
||||
# Fabien Devaux <fab AT gnux DOT info>
|
||||
@@ -41,6 +41,7 @@ import sys
|
||||
import threading
|
||||
import os
|
||||
import glob
|
||||
import urllib
|
||||
|
||||
import fix_encoding
|
||||
|
||||
@@ -138,7 +139,7 @@ if __name__ == '__main__':
|
||||
if cat not in CATEGORIES:
|
||||
raise SystemExit('Invalid category!')
|
||||
|
||||
what = '+'.join(sys.argv[3:])
|
||||
what = urllib.quote(' '.join(sys.argv[3:]))
|
||||
|
||||
threads = []
|
||||
for engine in engines_list:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
#VERSION: 1.21
|
||||
#VERSION: 1.23
|
||||
#AUTHORS: BTDigg team (research@btdigg.org)
|
||||
#
|
||||
# GNU GENERAL PUBLIC LICENSE
|
||||
@@ -36,7 +36,7 @@ class btdigg(object):
|
||||
pass
|
||||
|
||||
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)),))
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#VERSION: 1.53
|
||||
#VERSION: 2.00
|
||||
#AUTHORS: Fabien Devaux (fab@gnux.info)
|
||||
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
# Arthur (custparasite@gmx.se)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
@@ -27,94 +28,112 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
import sgmllib3
|
||||
from helpers import retrieve_url, download_file
|
||||
from html.parser import HTMLParser
|
||||
from helpers import download_file
|
||||
import urllib.request
|
||||
|
||||
PREVIOUS_IDS = set()
|
||||
|
||||
class piratebay(object):
|
||||
url = 'https://thepiratebay.se'
|
||||
name = 'The Pirate Bay'
|
||||
supported_categories = {'all': '0', 'movies': '200', 'music': '100', 'games': '400', 'software': '300'}
|
||||
url = 'http://thepiratebay.se'
|
||||
name = 'The Pirate Bay'
|
||||
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):
|
||||
print(download_file(info))
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
class MyHtmlParseWithBlackJack(HTMLParser):
|
||||
def __init__(self, results, url):
|
||||
super().__init__()
|
||||
self.url = url
|
||||
self.results = results
|
||||
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 }
|
||||
|
||||
class SimpleSGMLParser(sgmllib3.SGMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib3.SGMLParser.__init__(self)
|
||||
self.td_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
self.url = url
|
||||
self.code = 0
|
||||
self.in_name = None
|
||||
def handle_tag_a_ref(self, attrs):
|
||||
params = dict(attrs)
|
||||
#1
|
||||
if params['href'].startswith('/torrent/'):
|
||||
get_id = params['href'].split('/')[2]
|
||||
if not get_id in PREVIOUS_IDS:
|
||||
self.current_item = {}
|
||||
self.current_item['desc_link'] = self.url + params['href'].strip()
|
||||
self.current_item['name'] = params['title'][12:].strip()
|
||||
self.current_item['id'] = get_id
|
||||
#2
|
||||
elif (not self.current_item is None) and (params['href'].startswith('magnet:')):
|
||||
self.current_item['link'] = params['href'].strip()
|
||||
|
||||
def start_a(self, attr):
|
||||
params = dict(attr)
|
||||
if params['href'].startswith('/torrent/'):
|
||||
self.current_item = {}
|
||||
self.td_counter = 0
|
||||
self.current_item['desc_link'] = self.url + params['href'].strip()
|
||||
self.in_name = True
|
||||
self.current_item['id'] = params['href'].split('/')[2]
|
||||
elif params['href'].startswith('magnet:'):
|
||||
self.current_item['link']=params['href'].strip()
|
||||
self.in_name = False
|
||||
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_data(self, data):
|
||||
if self.td_counter == 0:
|
||||
if self.in_name:
|
||||
if 'name' not in self.current_item:
|
||||
self.current_item['name'] = ''
|
||||
self.current_item['name']+= data.strip()
|
||||
else:
|
||||
#Parse size
|
||||
if 'Size' in data:
|
||||
self.current_item['size'] = data[data.index("Size")+5:]
|
||||
self.current_item['size'] = self.current_item['size'][:self.current_item['size'].index(',')]
|
||||
elif self.td_counter == 1:
|
||||
if 'seeds' not in self.current_item:
|
||||
self.current_item['seeds'] = ''
|
||||
self.current_item['seeds']+= data.strip()
|
||||
elif self.td_counter == 2:
|
||||
if 'leech' not in self.current_item:
|
||||
self.current_item['leech'] = ''
|
||||
self.current_item['leech']+= data.strip()
|
||||
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 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
|
||||
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)
|
||||
PREVIOUS_IDS.add(self.current_item['id'])
|
||||
self.results.append('a')
|
||||
def search(self, what, cat='all'):
|
||||
ret = []
|
||||
i = 0
|
||||
order = 'se'
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url)
|
||||
dat = retrieve_url(self.url+'/search/%s/%d/7/%s' % (what, i, self.supported_categories[cat]))
|
||||
parser.feed(dat)
|
||||
parser.close()
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag in self.dispatcher:
|
||||
self.dispatcher[tag](attrs)
|
||||
|
||||
def handle_data(self, data):
|
||||
if not self.current_item is None:
|
||||
if self.size_found:
|
||||
#with utf-8 you're going to have something like that: ['Uploaded', '10-02'], ['15:31,', 'Size', '240.34'], ['MiB,', 'ULed', 'by']
|
||||
temp = data.split()
|
||||
if 'Size' in temp:
|
||||
sizeIn = temp.index('Size')
|
||||
self.current_item['size'] = temp[sizeIn + 1]
|
||||
self.size_found = False
|
||||
self.unit_found = True
|
||||
elif self.unit_found:
|
||||
temp = data.split()
|
||||
self.current_item['size'] = ' '.join((self.current_item['size'], temp[0]))
|
||||
self.unit_found = False
|
||||
elif self.seed_found:
|
||||
self.current_item['seeds'] += data.rstrip()
|
||||
elif self.leech_found:
|
||||
self.current_item['leech'] += data.rstrip()
|
||||
self.current_item['engine_url'] = self.url
|
||||
prettyPrinter(self.current_item)
|
||||
PREVIOUS_IDS.add(self.current_item['id'])
|
||||
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'):
|
||||
ret = []
|
||||
i = 0
|
||||
while i < 11:
|
||||
results = []
|
||||
parser = self.MyHtmlParseWithBlackJack(results, self.url)
|
||||
query = '%s/search/%s/%d/99/%s' % (self.url, what, i, self.supported_categories[cat])
|
||||
dat = urllib.request.urlopen(query)
|
||||
parser.feed(dat.read().decode('utf-8'))
|
||||
parser.close()
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#VERSION: 1.32
|
||||
#VERSION: 1.33
|
||||
#AUTHORS: Gekko Dam Beer (gekko04@users.sourceforge.net)
|
||||
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
# Bruno Barbieri (brunorex@gmail.com)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
@@ -27,8 +28,10 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
import sgmllib3
|
||||
from helpers import retrieve_url, download_file
|
||||
from urllib import error, parse
|
||||
from html.parser import HTMLParser
|
||||
import re
|
||||
|
||||
class torrentreactor(object):
|
||||
url = 'http://www.torrentreactor.net'
|
||||
@@ -37,30 +40,32 @@ class torrentreactor(object):
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
|
||||
class SimpleSGMLParser(sgmllib3.SGMLParser):
|
||||
|
||||
class SimpleHTMLParser(HTMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib3.SGMLParser.__init__(self)
|
||||
HTMLParser.__init__(self)
|
||||
self.td_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
self.id = None
|
||||
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):
|
||||
params = dict(attr)
|
||||
if 'torrentreactor.net/download.php' in params['href']:
|
||||
if re.match("/torrents/\d+.*", params['href']):
|
||||
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.current_item['link'] = params['href'].strip()
|
||||
elif params['href'].startswith('/torrents/'):
|
||||
self.current_item['desc_link'] = 'http://www.torrentreactor.net'+params['href'].strip()
|
||||
self.current_item['name'] = parse.unquote_plus(params['href'].split('&')[1].split('name=')[1])
|
||||
|
||||
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 'size' not in self.current_item:
|
||||
self.current_item['size'] = ''
|
||||
@@ -92,14 +97,20 @@ class torrentreactor(object):
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
self.parser = self.SimpleSGMLParser(self.results, self.url)
|
||||
self.parser = self.SimpleHTMLParser(self.results, self.url)
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
i = 0
|
||||
dat = ''
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(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)))
|
||||
parser = self.SimpleHTMLParser(results, self.url)
|
||||
|
||||
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.close()
|
||||
if len(results) <= 0:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
torrentreactor: 1.32
|
||||
torrentreactor: 1.33
|
||||
mininova: 1.50
|
||||
piratebay: 1.53
|
||||
piratebay: 2.00
|
||||
vertor: 1.3
|
||||
extratorrent: 1.2
|
||||
kickasstorrents: 1.24
|
||||
btdigg: 1.21
|
||||
btdigg: 1.23
|
||||
legittorrents: 1.02
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#VERSION: 1.23
|
||||
#VERSION: 1.24
|
||||
|
||||
# Author:
|
||||
# Fabien Devaux <fab AT gnux DOT info>
|
||||
@@ -134,7 +134,7 @@ if __name__ == '__main__':
|
||||
if cat not in CATEGORIES:
|
||||
raise SystemExit('Invalid category!')
|
||||
|
||||
what = urllib.parse.quote('+'.join(sys.argv[3:]))
|
||||
what = urllib.parse.quote(' '.join(sys.argv[3:]))
|
||||
|
||||
threads = []
|
||||
for engine in engines_list:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#VERSION: 1.43
|
||||
#VERSION: 1.44
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# 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
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
#import codecs
|
||||
|
||||
# Force UTF-8 printing
|
||||
#sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
|
||||
|
||||
def prettyPrinter(dictionary):
|
||||
# Convert everything to unicode for safe printing
|
||||
#for key,value in list(dictionary.items()):
|
||||
#if isinstance(dictionary[key], str):
|
||||
# dictionary[key] = str(dictionary[key], 'utf-8')
|
||||
outtext = ''
|
||||
dictionary['size'] = anySizeToBytes(dictionary['size'])
|
||||
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:
|
||||
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):
|
||||
"""
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
|
||||
#include "torrentcreatorthread.h"
|
||||
#include "fs_utils.h"
|
||||
#include "misc.h"
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 1600
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
@@ -130,7 +131,7 @@ void TorrentCreatorThread::run() {
|
||||
if (abort) return;
|
||||
// calculate the hash for all pieces
|
||||
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
|
||||
// torrent_info structure
|
||||
t.set_creator(creator_str.toUtf8().constData());
|
||||
@@ -156,6 +157,6 @@ void TorrentCreatorThread::run() {
|
||||
emit updateProgress(100);
|
||||
emit creationSuccess(save_path, parent_path);
|
||||
} catch (std::exception& e) {
|
||||
emit creationFailure(QString::fromLocal8Bit(e.what()));
|
||||
emit creationFailure(misc::toQStringU(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,15 +77,15 @@ void TorrentImportDlg::on_browseContentBtn_clicked()
|
||||
{
|
||||
QIniSettings settings;
|
||||
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;
|
||||
#if LIBTORRENT_VERSION_NUM >= 1600
|
||||
if (!multifile && has_parent_path(t->files().file_path(*(t->files().begin()))))
|
||||
multifile = true;
|
||||
if (!multifile && (QDir::fromNativeSeparators(misc::toQStringU(t->file_at(0).path)).indexOf('/') != -1))
|
||||
multifile = true;
|
||||
#else
|
||||
if (!multifile && t->file_at(0).path.has_parent_path())
|
||||
multifile = true;
|
||||
multifile = true;
|
||||
#endif
|
||||
|
||||
if (!multifile) {
|
||||
// Single file torrent
|
||||
#if LIBTORRENT_VERSION_NUM >= 1600
|
||||
|
||||
@@ -112,6 +112,7 @@ TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *main_window,
|
||||
#if defined(Q_WS_MAC)
|
||||
setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
#endif
|
||||
header()->setStretchLastSection(false);
|
||||
|
||||
// Default hidden columns
|
||||
if (!column_loaded) {
|
||||
|
||||
@@ -257,7 +257,7 @@ QByteArray btjson::getTrackersForTorrent(const QString& hash)
|
||||
tracker_list.append(tracker_dict);
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ QByteArray btjson::getPropertiesForTorrent(const QString& hash)
|
||||
const qreal ratio = QBtSession::instance()->getRealRatio(h.hash());
|
||||
data[KEY_PROP_RATIO] = ratio > 100. ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 1, false);
|
||||
} 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();
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ QByteArray btjson::getFilesForTorrent(const QString& hash)
|
||||
file_list.append(file_dict);
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +193,7 @@ void HttpConnection::respond() {
|
||||
if (nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) {
|
||||
m_generator.setStatusLine(403, "Forbidden");
|
||||
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());
|
||||
write();
|
||||
return;
|
||||
|
||||
@@ -19,7 +19,7 @@ XPStyle on
|
||||
!define CSIDL_APPDATA '0x1A' ;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_FUNCTION PageFinishRun
|
||||
!define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt)
|
||||
@@ -33,7 +33,7 @@ OutFile "qbittorrent_${PROG_VERSION}_setup.exe"
|
||||
;Installer Version Information
|
||||
VIAddVersionKey "ProductName" "qBittorrent"
|
||||
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 "FileVersion" "${PROG_VERSION}"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PROJECT_NAME = qbittorrent
|
||||
PROJECT_VERSION = 3.1.10
|
||||
PROJECT_VERSION = 3.1.12
|
||||
|
||||
os2 {
|
||||
DEFINES += VERSION=\'\"v$${PROJECT_VERSION}\"\'
|
||||
@@ -9,4 +9,4 @@ os2 {
|
||||
|
||||
DEFINES += VERSION_MAJOR=3
|
||||
DEFINES += VERSION_MINOR=1
|
||||
DEFINES += VERSION_BUGFIX=10
|
||||
DEFINES += VERSION_BUGFIX=12
|
||||
|
||||
Reference in New Issue
Block a user