Browse Source

Merge pull request #9883 from glassez/queue

Save torrents queue in separate file
adaptive-webui-19844
Vladimir Golovnev 6 years ago committed by GitHub
parent
commit
9e00f08ea6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      src/base/bittorrent/private/resumedatasavingmanager.cpp
  2. 11
      src/base/bittorrent/private/resumedatasavingmanager.h
  3. 202
      src/base/bittorrent/session.cpp
  4. 3
      src/base/bittorrent/session.h
  5. 2
      src/base/rss/rss_feed.cpp

29
src/base/bittorrent/private/resumedatasavingmanager.cpp

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015, 2018 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
@ -39,18 +39,23 @@ ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath @@ -39,18 +39,23 @@ ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath
{
}
void ResumeDataSavingManager::saveResumeData(QString infoHash, QByteArray data) const
void ResumeDataSavingManager::save(const QString &filename, const QByteArray &data) const
{
QString filename = QString("%1.fastresume").arg(infoHash);
QString filepath = m_resumeDataDir.absoluteFilePath(filename);
qDebug() << "Saving resume data in" << filepath;
QSaveFile resumeFile(filepath);
if (resumeFile.open(QIODevice::WriteOnly)) {
resumeFile.write(data);
if (!resumeFile.commit()) {
Logger::instance()->addMessage(QString("Couldn't save resume data in %1. Error: %2")
.arg(filepath, resumeFile.errorString()), Log::WARNING);
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QSaveFile file {filepath};
if (file.open(QIODevice::WriteOnly)) {
file.write(data);
if (!file.commit()) {
Logger::instance()->addMessage(QString("Couldn't save data in '%1'. Error: %2")
.arg(filepath, file.errorString()), Log::WARNING);
}
}
}
void ResumeDataSavingManager::remove(const QString &filename) const
{
const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
Utils::Fs::forceRemove(filepath);
}

11
src/base/bittorrent/private/resumedatasavingmanager.h

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2015, 2018 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
@ -26,8 +26,7 @@ @@ -26,8 +26,7 @@
* exception statement from your version.
*/
#ifndef RESUMEDATASAVINGMANAGER_H
#define RESUMEDATASAVINGMANAGER_H
#pragma once
#include <QByteArray>
#include <QDir>
@ -36,15 +35,15 @@ @@ -36,15 +35,15 @@
class ResumeDataSavingManager : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(ResumeDataSavingManager)
public:
explicit ResumeDataSavingManager(const QString &resumeFolderPath);
public slots:
void saveResumeData(QString infoHash, QByteArray data) const;
void save(const QString &filename, const QByteArray &data) const;
void remove(const QString &filename) const;
private:
QDir m_resumeDataDir;
};
#endif // RESUMEDATASAVINGMANAGER_H

202
src/base/bittorrent/session.cpp

@ -69,6 +69,7 @@ @@ -69,6 +69,7 @@
#endif
#include "base/algorithm.h"
#include "base/global.h"
#include "base/logger.h"
#include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h"
@ -532,7 +533,7 @@ Session::Session(QObject *parent) @@ -532,7 +533,7 @@ Session::Session(QObject *parent)
connect(&m_networkManager, &QNetworkConfigurationManager::configurationChanged, this, &Session::networkConfigurationChange);
m_ioThread = new QThread(this);
m_resumeDataSavingManager = new ResumeDataSavingManager(m_resumeFolderPath);
m_resumeDataSavingManager = new ResumeDataSavingManager {m_resumeFolderPath};
m_resumeDataSavingManager->moveToThread(m_ioThread);
connect(m_ioThread, &QThread::finished, m_resumeDataSavingManager, &QObject::deleteLater);
m_ioThread->start();
@ -1978,8 +1979,8 @@ bool Session::cancelLoadMetadata(const InfoHash &hash) @@ -1978,8 +1979,8 @@ bool Session::cancelLoadMetadata(const InfoHash &hash)
void Session::increaseTorrentsPriority(const QStringList &hashes)
{
std::priority_queue<QPair<int, TorrentHandle *>,
std::vector<QPair<int, TorrentHandle *> >,
std::greater<QPair<int, TorrentHandle *> > > torrentQueue;
std::vector<QPair<int, TorrentHandle *>>,
std::greater<QPair<int, TorrentHandle *>>> torrentQueue;
// Sort torrents by priority
foreach (const InfoHash &hash, hashes) {
@ -1995,14 +1996,14 @@ void Session::increaseTorrentsPriority(const QStringList &hashes) @@ -1995,14 +1996,14 @@ void Session::increaseTorrentsPriority(const QStringList &hashes)
torrentQueue.pop();
}
handleTorrentsPrioritiesChanged();
saveTorrentsQueue();
}
void Session::decreaseTorrentsPriority(const QStringList &hashes)
{
std::priority_queue<QPair<int, TorrentHandle *>,
std::vector<QPair<int, TorrentHandle *> >,
std::less<QPair<int, TorrentHandle *> > > torrentQueue;
std::vector<QPair<int, TorrentHandle *>>,
std::less<QPair<int, TorrentHandle *>>> torrentQueue;
// Sort torrents by priority
foreach (const InfoHash &hash, hashes) {
@ -2021,14 +2022,14 @@ void Session::decreaseTorrentsPriority(const QStringList &hashes) @@ -2021,14 +2022,14 @@ void Session::decreaseTorrentsPriority(const QStringList &hashes)
for (auto i = m_loadedMetadata.cbegin(); i != m_loadedMetadata.cend(); ++i)
torrentQueuePositionBottom(m_nativeSession->find_torrent(i.key()));
handleTorrentsPrioritiesChanged();
saveTorrentsQueue();
}
void Session::topTorrentsPriority(const QStringList &hashes)
{
std::priority_queue<QPair<int, TorrentHandle *>,
std::vector<QPair<int, TorrentHandle *> >,
std::greater<QPair<int, TorrentHandle *> > > torrentQueue;
std::vector<QPair<int, TorrentHandle *>>,
std::greater<QPair<int, TorrentHandle *>>> torrentQueue;
// Sort torrents by priority
foreach (const InfoHash &hash, hashes) {
@ -2044,14 +2045,14 @@ void Session::topTorrentsPriority(const QStringList &hashes) @@ -2044,14 +2045,14 @@ void Session::topTorrentsPriority(const QStringList &hashes)
torrentQueue.pop();
}
handleTorrentsPrioritiesChanged();
saveTorrentsQueue();
}
void Session::bottomTorrentsPriority(const QStringList &hashes)
{
std::priority_queue<QPair<int, TorrentHandle *>,
std::vector<QPair<int, TorrentHandle *> >,
std::less<QPair<int, TorrentHandle *> > > torrentQueue;
std::vector<QPair<int, TorrentHandle *>>,
std::less<QPair<int, TorrentHandle *>>> torrentQueue;
// Sort torrents by priority
foreach (const InfoHash &hash, hashes) {
@ -2070,7 +2071,7 @@ void Session::bottomTorrentsPriority(const QStringList &hashes) @@ -2070,7 +2071,7 @@ void Session::bottomTorrentsPriority(const QStringList &hashes)
for (auto i = m_loadedMetadata.cbegin(); i != m_loadedMetadata.cend(); ++i)
torrentQueuePositionBottom(m_nativeSession->find_torrent(i.key()));
handleTorrentsPrioritiesChanged();
saveTorrentsQueue();
}
QHash<InfoHash, TorrentHandle *> Session::torrents() const
@ -2379,11 +2380,13 @@ void Session::generateResumeData(bool final) @@ -2379,11 +2380,13 @@ void Session::generateResumeData(bool final)
// Called on exit
void Session::saveResumeData()
{
qDebug("Saving fast resume data...");
qDebug("Saving resume data...");
// Pause session
m_nativeSession->pause();
if (isQueueingSystemEnabled())
saveTorrentsQueue();
generateResumeData(true);
while (m_numResumeData > 0) {
@ -2408,6 +2411,31 @@ void Session::saveResumeData() @@ -2408,6 +2411,31 @@ void Session::saveResumeData()
}
}
void Session::saveTorrentsQueue()
{
QMap<int, QString> queue; // Use QMap since it should be ordered by key
for (const TorrentHandle *torrent : copyAsConst(torrents())) {
// We require actual (non-cached) queue position here!
const int queuePos = torrent->nativeHandle().queue_position();
if (queuePos >= 0)
queue[queuePos] = torrent->hash();
}
QByteArray data;
for (const QString &hash : qAsConst(queue))
data += (hash.toLatin1() + '\n');
const QString filename = QLatin1String {"queue"};
QMetaObject::invokeMethod(m_resumeDataSavingManager, "save"
, Q_ARG(QString, filename), Q_ARG(QByteArray, data));
}
void Session::removeTorrentsQueue()
{
const QString filename = QLatin1String {"queue"};
QMetaObject::invokeMethod(m_resumeDataSavingManager, "remove", Q_ARG(QString, filename));
}
void Session::setDefaultSavePath(QString path)
{
path = normalizeSavePath(path);
@ -3213,6 +3241,11 @@ void Session::setQueueingSystemEnabled(bool enabled) @@ -3213,6 +3241,11 @@ void Session::setQueueingSystemEnabled(bool enabled)
if (enabled != m_isQueueingEnabled) {
m_isQueueingEnabled = enabled;
configureDeferred();
if (enabled)
saveTorrentsQueue();
else
removeTorrentsQueue();
}
}
@ -3543,18 +3576,6 @@ void Session::handleTorrentShareLimitChanged(TorrentHandle *const torrent) @@ -3543,18 +3576,6 @@ void Session::handleTorrentShareLimitChanged(TorrentHandle *const torrent)
updateSeedingLimitTimer();
}
void Session::handleTorrentsPrioritiesChanged()
{
// Save fastresume for the torrents that changed queue position
for (TorrentHandle *const torrent : torrents()) {
if (!torrent->isSeed()) {
// cached vs actual queue position, qBt starts queue at 1
if (torrent->queuePosition() != (torrent->nativeHandle().queue_position() + 1))
saveTorrentResumeData(torrent);
}
}
}
void Session::saveTorrentResumeData(TorrentHandle *const torrent)
{
qDebug("Saving fastresume data for %s", qUtf8Printable(torrent->name()));
@ -3677,11 +3698,8 @@ void Session::handleTorrentChecked(TorrentHandle *const torrent) @@ -3677,11 +3698,8 @@ void Session::handleTorrentChecked(TorrentHandle *const torrent)
void Session::handleTorrentFinished(TorrentHandle *const torrent)
{
if (!torrent->hasError() && !torrent->hasMissingFiles()) {
if (!torrent->hasError() && !torrent->hasMissingFiles())
saveTorrentResumeData(torrent);
if (isQueueingSystemEnabled())
handleTorrentsPrioritiesChanged();
}
emit torrentFinished(torrent);
qDebug("Checking if the torrent contains torrent files to download");
@ -3724,8 +3742,9 @@ void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const l @@ -3724,8 +3742,9 @@ void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const l
QByteArray out;
libt::bencode(std::back_inserter(out), data);
QMetaObject::invokeMethod(m_resumeDataSavingManager, "saveResumeData",
Q_ARG(QString, torrent->hash()), Q_ARG(QByteArray, out));
const QString filename = QString("%1.fastresume").arg(torrent->hash());
QMetaObject::invokeMethod(m_resumeDataSavingManager, "save",
Q_ARG(QString, filename), Q_ARG(QByteArray, out));
}
void Session::handleTorrentResumeDataFailed(TorrentHandle *const torrent)
@ -3894,56 +3913,97 @@ void Session::startUpTorrents() @@ -3894,56 +3913,97 @@ void Session::startUpTorrents()
++resumedTorrentsCount;
};
qDebug("Starting up torrents");
qDebug("Starting up torrents...");
qDebug("Queue size: %d", fastresumes.size());
// Resume downloads
QMap<int, TorrentResumeData> queuedResumeData;
int nextQueuePosition = 1;
int numOfRemappedFiles = 0;
const QRegularExpression rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
foreach (const QString &fastresumeName, fastresumes) {
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
if (!rxMatch.hasMatch()) continue;
QString hash = rxMatch.captured(1);
QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
QByteArray data;
CreateTorrentParams torrentParams;
MagnetUri magnetUri;
int queuePosition;
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, torrentParams, queuePosition, magnetUri)) {
if (queuePosition <= nextQueuePosition) {
startupTorrent({ hash, magnetUri, torrentParams, data });
if (queuePosition == nextQueuePosition) {
++nextQueuePosition;
while (queuedResumeData.contains(nextQueuePosition)) {
startupTorrent(queuedResumeData.take(nextQueuePosition));
++nextQueuePosition;
if (isQueueingSystemEnabled()) {
QFile queueFile {resumeDataDir.absoluteFilePath(QLatin1String {"queue"})};
// TODO: The following code is deprecated in 4.1.5. Remove after several releases in 4.2.x.
// === BEGIN DEPRECATED CODE === //
if (!queueFile.exists()) {
// Resume downloads in a legacy manner
QMap<int, TorrentResumeData> queuedResumeData;
int nextQueuePosition = 1;
int numOfRemappedFiles = 0;
foreach (const QString &fastresumeName, fastresumes) {
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
if (!rxMatch.hasMatch()) continue;
QString hash = rxMatch.captured(1);
QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
QByteArray data;
CreateTorrentParams torrentParams;
MagnetUri magnetUri;
int queuePosition;
if (readFile(fastresumePath, data) && loadTorrentResumeData(data, torrentParams, queuePosition, magnetUri)) {
if (queuePosition <= nextQueuePosition) {
startupTorrent({ hash, magnetUri, torrentParams, data });
if (queuePosition == nextQueuePosition) {
++nextQueuePosition;
while (queuedResumeData.contains(nextQueuePosition)) {
startupTorrent(queuedResumeData.take(nextQueuePosition));
++nextQueuePosition;
}
}
}
else {
int q = queuePosition;
for (; queuedResumeData.contains(q); ++q) {}
if (q != queuePosition)
++numOfRemappedFiles;
queuedResumeData[q] = {hash, magnetUri, torrentParams, data};
}
}
}
else {
int q = queuePosition;
for (; queuedResumeData.contains(q); ++q) {
}
if (q != queuePosition) {
++numOfRemappedFiles;
}
queuedResumeData[q] = {hash, magnetUri, torrentParams, data};
if (numOfRemappedFiles > 0) {
logger->addMessage(
QString(tr("Queue positions were corrected in %1 resume files")).arg(numOfRemappedFiles),
Log::CRITICAL);
}
// starting up downloading torrents (queue position > 0)
foreach (const TorrentResumeData &torrentResumeData, queuedResumeData)
startupTorrent(torrentResumeData);
return;
}
}
// === END DEPRECATED CODE === //
if (numOfRemappedFiles > 0) {
logger->addMessage(
QString(tr("Queue positions were corrected in %1 resume files")).arg(numOfRemappedFiles),
Log::CRITICAL);
QStringList queue;
if (queueFile.open(QFile::ReadOnly)) {
QByteArray line;
while (!(line = queueFile.readLine()).isEmpty())
queue.append(QString::fromLatin1(line.trimmed()) + QLatin1String {".fastresume"});
}
else {
LogMsg(tr("Couldn't load torrents queue from '%1'. Error: %2")
.arg(queueFile.fileName(), queueFile.errorString()), Log::WARNING);
}
if (!queue.empty())
fastresumes = queue + fastresumes.toSet().subtract(queue.toSet()).toList();
}
// starting up downloading torrents (queue position > 0)
foreach (const TorrentResumeData &torrentResumeData, queuedResumeData)
startupTorrent(torrentResumeData);
for (const QString &fastresumeName : qAsConst(fastresumes)) {
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
if (!rxMatch.hasMatch()) continue;
const QString hash = rxMatch.captured(1);
const QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
QByteArray data;
CreateTorrentParams torrentParams;
MagnetUri magnetUri;
int queuePosition;
if (readFile(fastresumePath, data)
&& loadTorrentResumeData(data, torrentParams, queuePosition, magnetUri)) {
startupTorrent({hash, magnetUri, torrentParams, data});
}
}
}
quint64 Session::getAlltimeDL() const

3
src/base/bittorrent/session.h

@ -481,7 +481,6 @@ namespace BitTorrent @@ -481,7 +481,6 @@ namespace BitTorrent
// TorrentHandle interface
void handleTorrentShareLimitChanged(TorrentHandle *const torrent);
void handleTorrentsPrioritiesChanged();
void handleTorrentNameChanged(TorrentHandle *const torrent);
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
@ -633,6 +632,8 @@ namespace BitTorrent @@ -633,6 +632,8 @@ namespace BitTorrent
void createTorrentHandle(const libtorrent::torrent_handle &nativeHandle);
void saveResumeData();
void saveTorrentsQueue();
void removeTorrentsQueue();
#if LIBTORRENT_VERSION_NUM < 10100
void dispatchAlerts(libtorrent::alert *alertPtr);

2
src/base/rss/rss_feed.cpp

@ -474,7 +474,7 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles) @@ -474,7 +474,7 @@ int Feed::updateArticles(const QList<QVariantHash> &loadedArticles)
return (a1.first > a2.first);
});
if (sortData.size() > m_session->maxArticlesPerFeed())
if (sortData.size() > static_cast<uint>(m_session->maxArticlesPerFeed()))
sortData.resize(m_session->maxArticlesPerFeed());
int newArticlesCount = 0;

Loading…
Cancel
Save