mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-28 15:34:16 +00:00
Save torrents queue in separate file
This commit is contained in:
parent
e146c2f227
commit
68508ba657
@ -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
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
const 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);
|
||||
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);
|
||||
}
|
||||
|
@ -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 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef RESUMEDATASAVINGMANAGER_H
|
||||
#define RESUMEDATASAVINGMANAGER_H
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDir>
|
||||
@ -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
|
||||
|
@ -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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if (enabled != m_isQueueingEnabled) {
|
||||
m_isQueueingEnabled = enabled;
|
||||
configureDeferred();
|
||||
|
||||
if (enabled)
|
||||
saveTorrentsQueue();
|
||||
else
|
||||
removeTorrentsQueue();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
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
|
||||
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()
|
||||
++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) {
|
||||
|
||||
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};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 === //
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
for (const QString &fastresumeName : qAsConst(fastresumes)) {
|
||||
const QRegularExpressionMatch rxMatch = rx.match(fastresumeName);
|
||||
if (!rxMatch.hasMatch()) continue;
|
||||
|
||||
QString hash = rxMatch.captured(1);
|
||||
QString fastresumePath = resumeDataDir.absoluteFilePath(fastresumeName);
|
||||
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)) {
|
||||
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};
|
||||
}
|
||||
if (readFile(fastresumePath, data)
|
||||
&& loadTorrentResumeData(data, torrentParams, queuePosition, magnetUri)) {
|
||||
startupTorrent({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);
|
||||
}
|
||||
|
||||
quint64 Session::getAlltimeDL() const
|
||||
|
@ -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
|
||||
void createTorrentHandle(const libtorrent::torrent_handle &nativeHandle);
|
||||
|
||||
void saveResumeData();
|
||||
void saveTorrentsQueue();
|
||||
void removeTorrentsQueue();
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10100
|
||||
void dispatchAlerts(libtorrent::alert *alertPtr);
|
||||
|
Loading…
x
Reference in New Issue
Block a user