Browse Source

Save torrents queue in separate file

adaptive-webui-19844
Vladimir Golovnev (Glassez) 6 years ago
parent
commit
68508ba657
No known key found for this signature in database
GPG Key ID: 52A2C7DEE2DFA6F7
  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

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

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -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); const QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QString filepath = m_resumeDataDir.absoluteFilePath(filename);
QSaveFile file {filepath};
qDebug() << "Saving resume data in" << filepath; if (file.open(QIODevice::WriteOnly)) {
QSaveFile resumeFile(filepath); file.write(data);
if (resumeFile.open(QIODevice::WriteOnly)) { if (!file.commit()) {
resumeFile.write(data); Logger::instance()->addMessage(QString("Couldn't save data in '%1'. Error: %2")
if (!resumeFile.commit()) { .arg(filepath, file.errorString()), Log::WARNING);
Logger::instance()->addMessage(QString("Couldn't save resume data in %1. Error: %2")
.arg(filepath, resumeFile.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 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -26,8 +26,7 @@
* exception statement from your version. * exception statement from your version.
*/ */
#ifndef RESUMEDATASAVINGMANAGER_H #pragma once
#define RESUMEDATASAVINGMANAGER_H
#include <QByteArray> #include <QByteArray>
#include <QDir> #include <QDir>
@ -36,15 +35,15 @@
class ResumeDataSavingManager : public QObject class ResumeDataSavingManager : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(ResumeDataSavingManager)
public: public:
explicit ResumeDataSavingManager(const QString &resumeFolderPath); explicit ResumeDataSavingManager(const QString &resumeFolderPath);
public slots: 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: private:
QDir m_resumeDataDir; QDir m_resumeDataDir;
}; };
#endif // RESUMEDATASAVINGMANAGER_H

202
src/base/bittorrent/session.cpp

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

3
src/base/bittorrent/session.h

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

Loading…
Cancel
Save