WebAPI: Add a way to download .torrent file using search plugin

* Simplify nova2dl script
* Use search engine name instead of site URL (like nova2 does)
* Add a way to download torrent using search plugin

PR #20824.
This commit is contained in:
Vladimir Golovnev 2024-05-15 08:47:40 +03:00 committed by GitHub
parent 2c47f09d7a
commit 4d8713ce11
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 89 additions and 50 deletions

View File

@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018-2024 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -36,10 +36,10 @@
#include "base/utils/fs.h"
#include "searchpluginmanager.h"
SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QString &url, SearchPluginManager *manager)
: QObject {manager}
SearchDownloadHandler::SearchDownloadHandler(const QString &pluginName, const QString &url, SearchPluginManager *manager)
: QObject(manager)
, m_manager {manager}
, m_downloadProcess {new QProcess {this}}
, m_downloadProcess {new QProcess(this)}
{
m_downloadProcess->setEnvironment(QProcess::systemEnvironment());
connect(m_downloadProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished)
@ -48,7 +48,7 @@ SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QStri
{
Utils::ForeignApps::PYTHON_ISOLATE_MODE_FLAG,
(SearchPluginManager::engineLocation() / Path(u"nova2dl.py"_s)).toString(),
siteUrl,
pluginName,
url
};
// Launch search

View File

@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018-2024 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -41,7 +41,7 @@ class SearchDownloadHandler : public QObject
friend class SearchPluginManager;
SearchDownloadHandler(const QString &siteUrl, const QString &url, SearchPluginManager *manager);
SearchDownloadHandler(const QString &pluginName, const QString &url, SearchPluginManager *manager);
signals:
void downloadFinished(const QString &path);

View File

@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@ -61,13 +61,13 @@ namespace
}
SearchHandler::SearchHandler(const QString &pattern, const QString &category, const QStringList &usedPlugins, SearchPluginManager *manager)
: QObject {manager}
: QObject(manager)
, m_pattern {pattern}
, m_category {category}
, m_usedPlugins {usedPlugins}
, m_manager {manager}
, m_searchProcess {new QProcess {this}}
, m_searchTimeout {new QTimer {this}}
, m_searchProcess {new QProcess(this)}
, m_searchTimeout {new QTimer(this)}
{
// Load environment variables (proxy)
m_searchProcess->setEnvironment(QProcess::systemEnvironment());
@ -177,7 +177,8 @@ bool SearchHandler::parseSearchResult(const QStringView line, SearchResult &sear
const QList<QStringView> parts = line.split(u'|');
const int nbFields = parts.size();
if (nbFields <= PL_ENGINE_URL) return false; // Anything after ENGINE_URL is optional
if (nbFields <= PL_ENGINE_URL)
return false; // Anything after ENGINE_URL is optional
searchResult = SearchResult();
searchResult.fileUrl = parts.at(PL_DL_LINK).trimmed().toString(); // download URL
@ -194,7 +195,8 @@ bool SearchHandler::parseSearchResult(const QStringView line, SearchResult &sear
if (!ok || (searchResult.nbLeechers < 0))
searchResult.nbLeechers = -1;
searchResult.siteUrl = parts.at(PL_ENGINE_URL).trimmed().toString(); // Search site URL
searchResult.siteUrl = parts.at(PL_ENGINE_URL).trimmed().toString(); // Search engine site URL
searchResult.engineName = m_manager->pluginNameBySiteURL(searchResult.siteUrl); // Search engine name
if (nbFields > PL_DESC_LINK)
searchResult.descrLink = parts.at(PL_DESC_LINK).trimmed().toString(); // Description Link

View File

@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@ -46,6 +46,7 @@ struct SearchResult
qlonglong fileSize = 0;
qlonglong nbSeeders = 0;
qlonglong nbLeechers = 0;
QString engineName;
QString siteUrl;
QString descrLink;
QDateTime pubDate;

View File

@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@ -181,6 +181,17 @@ PluginInfo *SearchPluginManager::pluginInfo(const QString &name) const
return m_plugins.value(name);
}
QString SearchPluginManager::pluginNameBySiteURL(const QString &siteURL) const
{
for (const PluginInfo *plugin : asConst(m_plugins))
{
if (plugin->url == siteURL)
return plugin->name;
}
return {};
}
void SearchPluginManager::enablePlugin(const QString &name, const bool enabled)
{
PluginInfo *plugin = m_plugins.value(name, nullptr);
@ -338,9 +349,9 @@ void SearchPluginManager::checkForUpdates()
, this, &SearchPluginManager::versionInfoDownloadFinished);
}
SearchDownloadHandler *SearchPluginManager::downloadTorrent(const QString &siteUrl, const QString &url)
SearchDownloadHandler *SearchPluginManager::downloadTorrent(const QString &pluginName, const QString &url)
{
return new SearchDownloadHandler {siteUrl, url, this};
return new SearchDownloadHandler(pluginName, url, this);
}
SearchHandler *SearchPluginManager::startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins)

View File

@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015-2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
*
* This program is free software; you can redistribute it and/or
@ -75,6 +75,7 @@ public:
QStringList supportedCategories() const;
QStringList getPluginCategories(const QString &pluginName) const;
PluginInfo *pluginInfo(const QString &name) const;
QString pluginNameBySiteURL(const QString &siteURL) const;
void enablePlugin(const QString &name, bool enabled = true);
void updatePlugin(const QString &name);
@ -84,7 +85,7 @@ public:
void checkForUpdates();
SearchHandler *startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins);
SearchDownloadHandler *downloadTorrent(const QString &siteUrl, const QString &url);
SearchDownloadHandler *downloadTorrent(const QString &pluginName, const QString &url);
static PluginVersion getPluginVersion(const Path &filePath);
static QString categoryFullName(const QString &categoryName);

View File

@ -70,7 +70,8 @@ SearchJobWidget::SearchJobWidget(SearchHandler *searchHandler, IGUIApplication *
m_searchListModel->setHeaderData(SearchSortModel::SIZE, Qt::Horizontal, tr("Size", "i.e: file size"));
m_searchListModel->setHeaderData(SearchSortModel::SEEDS, Qt::Horizontal, tr("Seeders", "i.e: Number of full sources"));
m_searchListModel->setHeaderData(SearchSortModel::LEECHES, Qt::Horizontal, tr("Leechers", "i.e: Number of partial sources"));
m_searchListModel->setHeaderData(SearchSortModel::ENGINE_URL, Qt::Horizontal, tr("Search engine"));
m_searchListModel->setHeaderData(SearchSortModel::ENGINE_NAME, Qt::Horizontal, tr("Engine"));
m_searchListModel->setHeaderData(SearchSortModel::ENGINE_URL, Qt::Horizontal, tr("Engine URL"));
m_searchListModel->setHeaderData(SearchSortModel::PUB_DATE, Qt::Horizontal, tr("Published On"));
// Set columns text alignment
m_searchListModel->setHeaderData(SearchSortModel::SIZE, Qt::Horizontal, QVariant(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
@ -269,8 +270,8 @@ void SearchJobWidget::downloadTorrent(const QModelIndex &rowIndex, const AddTorr
{
const QString torrentUrl = m_proxyModel->data(
m_proxyModel->index(rowIndex.row(), SearchSortModel::DL_LINK)).toString();
const QString siteUrl = m_proxyModel->data(
m_proxyModel->index(rowIndex.row(), SearchSortModel::ENGINE_URL)).toString();
const QString engineName = m_proxyModel->data(
m_proxyModel->index(rowIndex.row(), SearchSortModel::ENGINE_NAME)).toString();
if (torrentUrl.startsWith(u"magnet:", Qt::CaseInsensitive))
{
@ -278,7 +279,7 @@ void SearchJobWidget::downloadTorrent(const QModelIndex &rowIndex, const AddTorr
}
else
{
SearchDownloadHandler *downloadHandler = m_searchHandler->manager()->downloadTorrent(siteUrl, torrentUrl);
SearchDownloadHandler *downloadHandler = m_searchHandler->manager()->downloadTorrent(engineName, torrentUrl);
connect(downloadHandler, &SearchDownloadHandler::downloadFinished
, this, [this, option](const QString &source) { addTorrentToSession(source, option); });
connect(downloadHandler, &SearchDownloadHandler::downloadFinished, downloadHandler, &SearchDownloadHandler::deleteLater);
@ -516,7 +517,7 @@ void SearchJobWidget::appendSearchResults(const QVector<SearchResult> &results)
m_searchListModel->insertRow(row);
const auto setModelData = [this, row] (const int column, const QString &displayData
, const QVariant &underlyingData, const Qt::Alignment textAlignmentData = {})
, const QVariant &underlyingData, const Qt::Alignment textAlignmentData = {})
{
const QMap<int, QVariant> data =
{
@ -529,6 +530,7 @@ void SearchJobWidget::appendSearchResults(const QVector<SearchResult> &results)
setModelData(SearchSortModel::NAME, result.fileName, result.fileName);
setModelData(SearchSortModel::DL_LINK, result.fileUrl, result.fileUrl);
setModelData(SearchSortModel::ENGINE_NAME, result.engineName, result.engineName);
setModelData(SearchSortModel::ENGINE_URL, result.siteUrl, result.siteUrl);
setModelData(SearchSortModel::DESC_LINK, result.descrLink, result.descrLink);
setModelData(SearchSortModel::SIZE, Utils::Misc::friendlyUnit(result.fileSize), result.fileSize, (Qt::AlignRight | Qt::AlignVCenter));

View File

@ -44,6 +44,7 @@ public:
SIZE,
SEEDS,
LEECHES,
ENGINE_NAME,
ENGINE_URL,
PUB_DATE,
DL_LINK,

View File

@ -1,7 +1,9 @@
#VERSION: 1.23
#VERSION: 1.24
# Author:
# Christophe DUMEZ (chris@qbittorrent.org)
# Contributors:
# Vladimir Golovnev (glassez@yandex.ru)
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@ -27,9 +29,7 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import glob
import importlib
import os
import pathlib
import sys
@ -40,34 +40,24 @@ if current_path not in sys.path:
from helpers import download_file
supported_engines = dict()
engines = glob.glob(os.path.join(os.path.dirname(__file__), 'engines', '*.py'))
for engine in engines:
e = engine.split(os.sep)[-1][:-3]
if len(e.strip()) == 0:
continue
if e.startswith('_'):
continue
try:
module = importlib.import_module("engines." + e)
engine_class = getattr(module, e)
globals()[e] = engine_class
engine_url = getattr(engine_class, 'url')
supported_engines[engine_url] = e
except Exception:
pass
if __name__ == '__main__':
if len(sys.argv) < 3:
raise SystemExit('./nova2dl.py engine_url download_parameter')
engine_url = sys.argv[1].strip()
raise SystemExit('./nova2dl.py engine_name download_parameter')
engine_name = sys.argv[1].strip()
download_param = sys.argv[2].strip()
if engine_url not in supported_engines.keys():
raise SystemExit('./nova2dl.py: this engine_url was not recognized')
engine = globals()[supported_engines[engine_url]]()
try:
module = importlib.import_module("engines." + engine_name)
engine_class = getattr(module, engine_name)
engine = engine_class()
except Exception as e:
print(repr(e))
raise SystemExit('./nova2dl.py: this engine_name was not recognized')
if hasattr(engine, 'download_torrent'):
engine.download_torrent(download_param)
else:
print(download_file(download_param))
sys.exit(0)

View File

@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
*
* This program is free software; you can redistribute it and/or
@ -36,8 +37,11 @@
#include <QList>
#include <QSharedPointer>
#include "base/addtorrentmanager.h"
#include "base/global.h"
#include "base/interfaces/iapplication.h"
#include "base/logger.h"
#include "base/search/searchdownloadhandler.h"
#include "base/search/searchhandler.h"
#include "base/utils/datetime.h"
#include "base/utils/foreignapps.h"
@ -213,6 +217,29 @@ void SearchController::deleteAction()
m_searchHandlers.erase(iter);
}
void SearchController::downloadTorrentAction()
{
requireParams({u"torrentUrl"_s, u"pluginName"_s});
const QString torrentUrl = params()[u"torrentUrl"_s];
const QString pluginName = params()[u"pluginName"_s];
if (torrentUrl.startsWith(u"magnet:", Qt::CaseInsensitive))
{
app()->addTorrentManager()->addTorrent(torrentUrl);
}
else
{
SearchDownloadHandler *downloadHandler = SearchPluginManager::instance()->downloadTorrent(pluginName, torrentUrl);
connect(downloadHandler, &SearchDownloadHandler::downloadFinished
, this, [this, downloadHandler](const QString &source)
{
app()->addTorrentManager()->addTorrent(source);
downloadHandler->deleteLater();
});
}
}
void SearchController::pluginsAction()
{
const QStringList allPlugins = SearchPluginManager::instance()->allPlugins();
@ -300,6 +327,7 @@ int SearchController::generateSearchId() const
* - "fileSize"
* - "nbSeeders"
* - "nbLeechers"
* - "engineName"
* - "siteUrl"
* - "descrLink"
* - "pubDate"
@ -316,6 +344,7 @@ QJsonObject SearchController::getResults(const QList<SearchResult> &searchResult
{u"fileSize"_s, searchResult.fileSize},
{u"nbSeeders"_s, searchResult.nbSeeders},
{u"nbLeechers"_s, searchResult.nbLeechers},
{u"engineName"_s, searchResult.engineName},
{u"siteUrl"_s, searchResult.siteUrl},
{u"descrLink"_s, searchResult.descrLink},
{u"pubDate"_s, Utils::DateTime::toSecsSinceEpoch(searchResult.pubDate)}

View File

@ -1,5 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2018 Thomas Piccirello <thomas.piccirello@gmail.com>
*
* This program is free software; you can redistribute it and/or
@ -56,6 +57,7 @@ private slots:
void statusAction();
void resultsAction();
void deleteAction();
void downloadTorrentAction();
void pluginsAction();
void installPluginAction();
void uninstallPluginAction();