mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-03 14:12:30 -06:00
Restore BitTorrent session asynchronously
Reduce the total startup time of the application and maintain sufficient responsiveness of the UI during startup due to the following: 1. Load resume data from disk asynchronously in separate thread; 2. Split handling of loaded resume data in chunks; 3. Reduce the number of emitting signals. PR #16840.
This commit is contained in:
committed by
GitHub
parent
ec1d2cba40
commit
be7cfb78de
@@ -32,7 +32,6 @@
|
||||
#include <QIcon>
|
||||
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/bittorrent/torrent.h"
|
||||
#include "base/global.h"
|
||||
#include "uithememanager.h"
|
||||
|
||||
@@ -181,7 +180,7 @@ CategoryFilterModel::CategoryFilterModel(QObject *parent)
|
||||
connect(session, &Session::categoryRemoved, this, &CategoryFilterModel::categoryRemoved);
|
||||
connect(session, &Session::torrentCategoryChanged, this, &CategoryFilterModel::torrentCategoryChanged);
|
||||
connect(session, &Session::subcategoriesSupportChanged, this, &CategoryFilterModel::subcategoriesSupportChanged);
|
||||
connect(session, &Session::torrentLoaded, this, &CategoryFilterModel::torrentAdded);
|
||||
connect(session, &Session::torrentsLoaded, this, &CategoryFilterModel::torrentsLoaded);
|
||||
connect(session, &Session::torrentAboutToBeRemoved, this, &CategoryFilterModel::torrentAboutToBeRemoved);
|
||||
|
||||
populate();
|
||||
@@ -333,13 +332,16 @@ void CategoryFilterModel::categoryRemoved(const QString &categoryName)
|
||||
}
|
||||
}
|
||||
|
||||
void CategoryFilterModel::torrentAdded(BitTorrent::Torrent *const torrent)
|
||||
void CategoryFilterModel::torrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents)
|
||||
{
|
||||
CategoryModelItem *item = findItem(torrent->category());
|
||||
Q_ASSERT(item);
|
||||
for (const BitTorrent::Torrent *torrent : torrents)
|
||||
{
|
||||
CategoryModelItem *item = findItem(torrent->category());
|
||||
Q_ASSERT(item);
|
||||
|
||||
item->increaseTorrentsCount();
|
||||
m_rootItem->childAt(0)->increaseTorrentsCount();
|
||||
item->increaseTorrentsCount();
|
||||
m_rootItem->childAt(0)->increaseTorrentsCount();
|
||||
}
|
||||
}
|
||||
|
||||
void CategoryFilterModel::torrentAboutToBeRemoved(BitTorrent::Torrent *const torrent)
|
||||
|
||||
@@ -28,17 +28,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtContainerFwd>
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
#include "base/bittorrent/torrent.h"
|
||||
|
||||
class QModelIndex;
|
||||
|
||||
class CategoryModelItem;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class Torrent;
|
||||
}
|
||||
|
||||
class CategoryFilterModel final : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -64,7 +62,7 @@ public:
|
||||
private slots:
|
||||
void categoryAdded(const QString &categoryName);
|
||||
void categoryRemoved(const QString &categoryName);
|
||||
void torrentAdded(BitTorrent::Torrent *const torrent);
|
||||
void torrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents);
|
||||
void torrentAboutToBeRemoved(BitTorrent::Torrent *const torrent);
|
||||
void torrentCategoryChanged(BitTorrent::Torrent *const torrent, const QString &oldCategory);
|
||||
void subcategoriesSupportChanged();
|
||||
|
||||
@@ -1205,12 +1205,12 @@ void MainWindow::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->matches(QKeySequence::Paste))
|
||||
{
|
||||
const QMimeData *mimeData {QGuiApplication::clipboard()->mimeData()};
|
||||
const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData();
|
||||
|
||||
if (mimeData->hasText())
|
||||
{
|
||||
const bool useTorrentAdditionDialog {AddNewTorrentDialog::isEnabled()};
|
||||
const QStringList lines {mimeData->text().split(u'\n', Qt::SkipEmptyParts)};
|
||||
const bool useTorrentAdditionDialog = AddNewTorrentDialog::isEnabled();
|
||||
const QStringList lines = mimeData->text().split(u'\n', Qt::SkipEmptyParts);
|
||||
|
||||
for (QString line : lines)
|
||||
{
|
||||
@@ -1438,6 +1438,7 @@ void MainWindow::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
for (const QString &mime : asConst(event->mimeData()->formats()))
|
||||
qDebug("mimeData: %s", mime.toLocal8Bit().data());
|
||||
|
||||
if (event->mimeData()->hasFormat(u"text/plain"_qs) || event->mimeData()->hasFormat(u"text/uri-list"_qs))
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ void SearchJobWidget::cancelSearch()
|
||||
|
||||
void SearchJobWidget::downloadTorrents(const AddTorrentOption option)
|
||||
{
|
||||
const QModelIndexList rows {m_ui->resultsBrowser->selectionModel()->selectedRows()};
|
||||
const QModelIndexList rows = m_ui->resultsBrowser->selectionModel()->selectedRows();
|
||||
for (const QModelIndex &rowIndex : rows)
|
||||
downloadTorrent(rowIndex, option);
|
||||
}
|
||||
@@ -390,10 +390,10 @@ void SearchJobWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||
auto *menu = new QMenu(this);
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"kt-set-max-download-speed"_qs), tr("Open download window")
|
||||
, this, [this]() { downloadTorrents(AddTorrentOption::ShowDialog); });
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"downloading"_qs), tr("Download")
|
||||
, this, [this]() { downloadTorrents(AddTorrentOption::SkipDialog); });
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"kt-set-max-download-speed"_qs)
|
||||
, tr("Open download window"), this, [this]() { downloadTorrents(AddTorrentOption::ShowDialog); });
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"downloading"_qs)
|
||||
, tr("Download"), this, [this]() { downloadTorrents(AddTorrentOption::SkipDialog); });
|
||||
menu->addSeparator();
|
||||
menu->addAction(UIThemeManager::instance()->getIcon(u"application-x-mswinurl"_qs), tr("Open description page")
|
||||
, this, &SearchJobWidget::openTorrentPages);
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include <QVector>
|
||||
|
||||
#include "base/bittorrent/session.h"
|
||||
#include "base/bittorrent/torrent.h"
|
||||
#include "base/global.h"
|
||||
#include "uithememanager.h"
|
||||
|
||||
@@ -99,7 +98,7 @@ TagFilterModel::TagFilterModel(QObject *parent)
|
||||
connect(session, &Session::tagRemoved, this, &TagFilterModel::tagRemoved);
|
||||
connect(session, &Session::torrentTagAdded, this, &TagFilterModel::torrentTagAdded);
|
||||
connect(session, &Session::torrentTagRemoved, this, &TagFilterModel::torrentTagRemoved);
|
||||
connect(session, &Session::torrentLoaded, this, &TagFilterModel::torrentAdded);
|
||||
connect(session, &Session::torrentsLoaded, this, &TagFilterModel::torrentsLoaded);
|
||||
connect(session, &Session::torrentAboutToBeRemoved, this, &TagFilterModel::torrentAboutToBeRemoved);
|
||||
populate();
|
||||
}
|
||||
@@ -230,16 +229,19 @@ void TagFilterModel::torrentTagRemoved(BitTorrent::Torrent *const torrent, const
|
||||
emit dataChanged(i, i);
|
||||
}
|
||||
|
||||
void TagFilterModel::torrentAdded(BitTorrent::Torrent *const torrent)
|
||||
void TagFilterModel::torrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents)
|
||||
{
|
||||
allTagsItem()->increaseTorrentsCount();
|
||||
for (const BitTorrent::Torrent *torrent : torrents)
|
||||
{
|
||||
allTagsItem()->increaseTorrentsCount();
|
||||
|
||||
const QVector<TagModelItem *> items = findItems(torrent->tags());
|
||||
if (items.isEmpty())
|
||||
untaggedItem()->increaseTorrentsCount();
|
||||
const QVector<TagModelItem *> items = findItems(torrent->tags());
|
||||
if (items.isEmpty())
|
||||
untaggedItem()->increaseTorrentsCount();
|
||||
|
||||
for (TagModelItem *item : items)
|
||||
item->increaseTorrentsCount();
|
||||
for (TagModelItem *item : items)
|
||||
item->increaseTorrentsCount();
|
||||
}
|
||||
}
|
||||
|
||||
void TagFilterModel::torrentAboutToBeRemoved(BitTorrent::Torrent *const torrent)
|
||||
|
||||
@@ -28,20 +28,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QtContainerFwd>
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
#include "base/bittorrent/torrent.h"
|
||||
#include "base/tagset.h"
|
||||
|
||||
class QModelIndex;
|
||||
|
||||
class TagModelItem;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class Torrent;
|
||||
}
|
||||
|
||||
class TagFilterModel final : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -67,7 +63,7 @@ private slots:
|
||||
void tagRemoved(const QString &tag);
|
||||
void torrentTagAdded(BitTorrent::Torrent *const torrent, const QString &tag);
|
||||
void torrentTagRemoved(BitTorrent::Torrent *const, const QString &tag);
|
||||
void torrentAdded(BitTorrent::Torrent *const torrent);
|
||||
void torrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents);
|
||||
void torrentAboutToBeRemoved(BitTorrent::Torrent *const torrent);
|
||||
|
||||
private:
|
||||
|
||||
@@ -134,8 +134,8 @@ BaseFilterWidget::BaseFilterWidget(QWidget *parent, TransferListWidget *transfer
|
||||
connect(this, &BaseFilterWidget::customContextMenuRequested, this, &BaseFilterWidget::showMenu);
|
||||
connect(this, &BaseFilterWidget::currentRowChanged, this, &BaseFilterWidget::applyFilter);
|
||||
|
||||
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentLoaded
|
||||
, this, &BaseFilterWidget::handleNewTorrent);
|
||||
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentsLoaded
|
||||
, this, &BaseFilterWidget::handleTorrentsLoaded);
|
||||
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentAboutToBeRemoved
|
||||
, this, &BaseFilterWidget::torrentAboutToBeDeleted);
|
||||
}
|
||||
@@ -318,9 +318,11 @@ void StatusFilterWidget::applyFilter(int row)
|
||||
transferList->applyStatusFilter(row);
|
||||
}
|
||||
|
||||
void StatusFilterWidget::handleNewTorrent(BitTorrent::Torrent *const torrent)
|
||||
void StatusFilterWidget::handleTorrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents)
|
||||
{
|
||||
updateTorrentStatus(torrent);
|
||||
for (const BitTorrent::Torrent *torrent : torrents)
|
||||
updateTorrentStatus(torrent);
|
||||
|
||||
updateTexts();
|
||||
}
|
||||
|
||||
@@ -376,6 +378,8 @@ TrackerFiltersList::TrackerFiltersList(QWidget *parent, TransferListWidget *tran
|
||||
|
||||
m_trackers[NULL_HOST] = {{}, noTracker};
|
||||
|
||||
handleTorrentsLoaded(BitTorrent::Session::instance()->torrents());
|
||||
|
||||
setCurrentRow(0, QItemSelectionModel::SelectCurrent);
|
||||
toggleFilter(Preferences::instance()->getTrackerFilterState());
|
||||
}
|
||||
@@ -390,7 +394,7 @@ void TrackerFiltersList::addTrackers(const BitTorrent::Torrent *torrent, const Q
|
||||
{
|
||||
const BitTorrent::TorrentID torrentID = torrent->id();
|
||||
for (const BitTorrent::TrackerEntry &tracker : trackers)
|
||||
addItem(tracker.url, torrentID);
|
||||
addItems(tracker.url, {torrentID});
|
||||
}
|
||||
|
||||
void TrackerFiltersList::removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers)
|
||||
@@ -431,12 +435,12 @@ void TrackerFiltersList::refreshTrackers(const BitTorrent::Torrent *torrent)
|
||||
const bool isTrackerless = trackerEntries.isEmpty();
|
||||
if (isTrackerless)
|
||||
{
|
||||
addItem(NULL_HOST, torrentID);
|
||||
addItems(NULL_HOST, {torrentID});
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const BitTorrent::TrackerEntry &trackerEntry : trackerEntries)
|
||||
addItem(trackerEntry.url, torrentID);
|
||||
addItems(trackerEntry.url, {torrentID});
|
||||
}
|
||||
|
||||
updateGeometry();
|
||||
@@ -445,23 +449,20 @@ void TrackerFiltersList::refreshTrackers(const BitTorrent::Torrent *torrent)
|
||||
void TrackerFiltersList::changeTrackerless(const BitTorrent::Torrent *torrent, const bool trackerless)
|
||||
{
|
||||
if (trackerless)
|
||||
addItem(NULL_HOST, torrent->id());
|
||||
addItems(NULL_HOST, {torrent->id()});
|
||||
else
|
||||
removeItem(NULL_HOST, torrent->id());
|
||||
}
|
||||
|
||||
void TrackerFiltersList::addItem(const QString &tracker, const BitTorrent::TorrentID &id)
|
||||
void TrackerFiltersList::addItems(const QString &trackerURL, const QVector<BitTorrent::TorrentID> &torrents)
|
||||
{
|
||||
const QString host = getHost(tracker);
|
||||
const QString host = getHost(trackerURL);
|
||||
auto trackersIt = m_trackers.find(host);
|
||||
const bool exists = (trackersIt != m_trackers.end());
|
||||
QListWidgetItem *trackerItem = nullptr;
|
||||
|
||||
if (exists)
|
||||
{
|
||||
if (trackersIt->torrents.contains(id))
|
||||
return;
|
||||
|
||||
trackerItem = trackersIt->item;
|
||||
}
|
||||
else
|
||||
@@ -469,17 +470,18 @@ void TrackerFiltersList::addItem(const QString &tracker, const BitTorrent::Torre
|
||||
trackerItem = new QListWidgetItem();
|
||||
trackerItem->setData(Qt::DecorationRole, UIThemeManager::instance()->getIcon(u"trackers"_qs));
|
||||
|
||||
TrackerData trackerData {{}, trackerItem};
|
||||
const TrackerData trackerData {{}, trackerItem};
|
||||
trackersIt = m_trackers.insert(host, trackerData);
|
||||
|
||||
const QString scheme = getScheme(tracker);
|
||||
const QString scheme = getScheme(trackerURL);
|
||||
downloadFavicon(u"%1://%2/favicon.ico"_qs.arg((scheme.startsWith(u"http") ? scheme : u"http"_qs), host));
|
||||
}
|
||||
|
||||
Q_ASSERT(trackerItem);
|
||||
|
||||
QSet<BitTorrent::TorrentID> &torrentIDs = trackersIt->torrents;
|
||||
torrentIDs.insert(id);
|
||||
for (const BitTorrent::TorrentID &torrentID : torrents)
|
||||
torrentIDs.insert(torrentID);
|
||||
|
||||
trackerItem->setText(u"%1 (%2)"_qs.arg(((host == NULL_HOST) ? tr("Trackerless") : host), QString::number(torrentIDs.size())));
|
||||
if (exists)
|
||||
@@ -724,18 +726,30 @@ void TrackerFiltersList::applyFilter(const int row)
|
||||
transferList->applyTrackerFilter(getTorrentIDs(row));
|
||||
}
|
||||
|
||||
void TrackerFiltersList::handleNewTorrent(BitTorrent::Torrent *const torrent)
|
||||
void TrackerFiltersList::handleTorrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents)
|
||||
{
|
||||
const BitTorrent::TorrentID torrentID = torrent->id();
|
||||
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
|
||||
for (const BitTorrent::TrackerEntry &tracker : trackers)
|
||||
addItem(tracker.url, torrentID);
|
||||
QHash<QString, QVector<BitTorrent::TorrentID>> torrentsPerTracker;
|
||||
for (const BitTorrent::Torrent *torrent : torrents)
|
||||
{
|
||||
const BitTorrent::TorrentID torrentID = torrent->id();
|
||||
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
|
||||
for (const BitTorrent::TrackerEntry &tracker : trackers)
|
||||
torrentsPerTracker[tracker.url].append(torrentID);
|
||||
|
||||
// Check for trackerless torrent
|
||||
if (trackers.isEmpty())
|
||||
addItem(NULL_HOST, torrentID);
|
||||
// Check for trackerless torrent
|
||||
if (trackers.isEmpty())
|
||||
torrentsPerTracker[NULL_HOST].append(torrentID);
|
||||
}
|
||||
|
||||
item(ALL_ROW)->setText(tr("All (%1)", "this is for the tracker filter").arg(++m_totalTorrents));
|
||||
for (auto it = torrentsPerTracker.cbegin(); it != torrentsPerTracker.cend(); ++it)
|
||||
{
|
||||
const QString &trackerURL = it.key();
|
||||
const QVector<BitTorrent::TorrentID> &torrents = it.value();
|
||||
addItems(trackerURL, torrents);
|
||||
}
|
||||
|
||||
m_totalTorrents += torrents.count();
|
||||
item(ALL_ROW)->setText(tr("All (%1)", "this is for the tracker filter").arg(m_totalTorrents));
|
||||
}
|
||||
|
||||
void TrackerFiltersList::torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent)
|
||||
|
||||
@@ -71,7 +71,7 @@ protected:
|
||||
private slots:
|
||||
virtual void showMenu() = 0;
|
||||
virtual void applyFilter(int row) = 0;
|
||||
virtual void handleNewTorrent(BitTorrent::Torrent *const) = 0;
|
||||
virtual void handleTorrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents) = 0;
|
||||
virtual void torrentAboutToBeDeleted(BitTorrent::Torrent *const) = 0;
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ private:
|
||||
// No need to redeclare them here as slots.
|
||||
void showMenu() override;
|
||||
void applyFilter(int row) override;
|
||||
void handleNewTorrent(BitTorrent::Torrent *const) override;
|
||||
void handleTorrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents) override;
|
||||
void torrentAboutToBeDeleted(BitTorrent::Torrent *const) override;
|
||||
|
||||
void populate();
|
||||
@@ -139,10 +139,10 @@ private:
|
||||
// No need to redeclare them here as slots.
|
||||
void showMenu() override;
|
||||
void applyFilter(int row) override;
|
||||
void handleNewTorrent(BitTorrent::Torrent *const torrent) override;
|
||||
void handleTorrentsLoaded(const QVector<BitTorrent::Torrent *> &torrents) override;
|
||||
void torrentAboutToBeDeleted(BitTorrent::Torrent *const torrent) override;
|
||||
|
||||
void addItem(const QString &tracker, const BitTorrent::TorrentID &id);
|
||||
void addItems(const QString &trackerURL, const QVector<BitTorrent::TorrentID> &torrents);
|
||||
void removeItem(const QString &trackerURL, const BitTorrent::TorrentID &id);
|
||||
QString trackerFromRow(int row) const;
|
||||
int rowFromTracker(const QString &tracker) const;
|
||||
|
||||
@@ -166,11 +166,10 @@ TransferListModel::TransferListModel(QObject *parent)
|
||||
|
||||
// Load the torrents
|
||||
using namespace BitTorrent;
|
||||
for (Torrent *const torrent : asConst(Session::instance()->torrents()))
|
||||
addTorrent(torrent);
|
||||
addTorrents(Session::instance()->torrents());
|
||||
|
||||
// Listen for torrent changes
|
||||
connect(Session::instance(), &Session::torrentLoaded, this, &TransferListModel::addTorrent);
|
||||
connect(Session::instance(), &Session::torrentsLoaded, this, &TransferListModel::addTorrents);
|
||||
connect(Session::instance(), &Session::torrentAboutToBeRemoved, this, &TransferListModel::handleTorrentAboutToBeRemoved);
|
||||
connect(Session::instance(), &Session::torrentsUpdated, this, &TransferListModel::handleTorrentsUpdated);
|
||||
|
||||
@@ -599,15 +598,19 @@ bool TransferListModel::setData(const QModelIndex &index, const QVariant &value,
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransferListModel::addTorrent(BitTorrent::Torrent *const torrent)
|
||||
void TransferListModel::addTorrents(const QVector<BitTorrent::Torrent *> &torrents)
|
||||
{
|
||||
Q_ASSERT(!m_torrentMap.contains(torrent));
|
||||
int row = m_torrentList.size();
|
||||
beginInsertRows({}, row, (row + torrents.size()));
|
||||
|
||||
const int row = m_torrentList.size();
|
||||
for (BitTorrent::Torrent *torrent : torrents)
|
||||
{
|
||||
Q_ASSERT(!m_torrentMap.contains(torrent));
|
||||
|
||||
m_torrentList.append(torrent);
|
||||
m_torrentMap[torrent] = row++;
|
||||
}
|
||||
|
||||
beginInsertRows({}, row, row);
|
||||
m_torrentList << torrent;
|
||||
m_torrentMap[torrent] = row;
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
BitTorrent::Torrent *torrentHandle(const QModelIndex &index) const;
|
||||
|
||||
private slots:
|
||||
void addTorrent(BitTorrent::Torrent *const torrent);
|
||||
void addTorrents(const QVector<BitTorrent::Torrent *> &torrents);
|
||||
void handleTorrentAboutToBeRemoved(BitTorrent::Torrent *const torrent);
|
||||
void handleTorrentStatusUpdated(BitTorrent::Torrent *const torrent);
|
||||
void handleTorrentsUpdated(const QVector<BitTorrent::Torrent *> &torrents);
|
||||
|
||||
Reference in New Issue
Block a user