2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2024 Radu Carpa <radu.carpa@cern.ch>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * In addition, as a special exception, the copyright holders give permission to
21 * link this program with the OpenSSL project's "OpenSSL" library (or with
22 * modified versions of it that use the same license as the "OpenSSL" library),
23 * and distribute the linked executables. You must obey the GNU General Public
24 * License in all respects for all of the code used other than "OpenSSL". If you
25 * modify file(s), you may extend this exception to your version of the file(s),
26 * but you are not obligated to do so. If you do not wish to do so, delete this
27 * exception statement from your version.
30 #include "torrentcreatorcontroller.h"
33 #include <QJsonObject>
34 #include <QStringList>
36 #include "base/global.h"
37 #include "base/bittorrent/torrentcreationmanager.h"
38 #include "base/preferences.h"
39 #include "base/utils/io.h"
40 #include "base/utils/string.h"
43 const QString KEY_COMMENT
= u
"comment"_s
;
44 const QString KEY_ERROR_MESSAGE
= u
"errorMessage"_s
;
45 const QString KEY_FORMAT
= u
"format"_s
;
46 const QString KEY_OPTIMIZE_ALIGNMENT
= u
"optimizeAlignment"_s
;
47 const QString KEY_PADDED_FILE_SIZE_LIMIT
= u
"paddedFileSizeLimit"_s
;
48 const QString KEY_PIECE_SIZE
= u
"pieceSize"_s
;
49 const QString KEY_PRIVATE
= u
"private"_s
;
50 const QString KEY_PROGRESS
= u
"progress"_s
;
51 const QString KEY_SOURCE
= u
"source"_s
;
52 const QString KEY_SOURCE_PATH
= u
"sourcePath"_s
;
53 const QString KEY_STATUS
= u
"status"_s
;
54 const QString KEY_TASK_ID
= u
"taskID"_s
;
55 const QString KEY_TIME_ADDED
= u
"timeAdded"_s
;
56 const QString KEY_TIME_FINISHED
= u
"timeFinished"_s
;
57 const QString KEY_TIME_STARTED
= u
"timeStarted"_s
;
58 const QString KEY_TORRENT_FILE_PATH
= u
"torrentFilePath"_s
;
59 const QString KEY_TRACKERS
= u
"trackers"_s
;
60 const QString KEY_URL_SEEDS
= u
"urlSeeds"_s
;
64 using Utils::String::parseBool
;
65 using Utils::String::parseInt
;
67 #ifdef QBT_USES_LIBTORRENT2
68 BitTorrent::TorrentFormat
parseTorrentFormat(const QString
&str
)
71 return BitTorrent::TorrentFormat::V1
;
73 return BitTorrent::TorrentFormat::V2
;
74 return BitTorrent::TorrentFormat::Hybrid
;
77 QString
torrentFormatToString(const BitTorrent::TorrentFormat torrentFormat
)
79 switch (torrentFormat
)
81 case BitTorrent::TorrentFormat::V1
:
83 case BitTorrent::TorrentFormat::V2
:
91 QString
taskStatusString(const std::shared_ptr
<BitTorrent::TorrentCreationTask
> task
)
96 switch (task
->state())
98 case BitTorrent::TorrentCreationTask::Queued
:
101 case BitTorrent::TorrentCreationTask::Running
:
103 case BitTorrent::TorrentCreationTask::Finished
:
104 return u
"Finished"_s
;
109 TorrentCreatorController::TorrentCreatorController(BitTorrent::TorrentCreationManager
*torrentCreationManager
, IApplication
*app
, QObject
*parent
)
110 : APIController(app
, parent
)
111 , m_torrentCreationManager
{torrentCreationManager
}
115 void TorrentCreatorController::addTaskAction()
117 requireParams({KEY_SOURCE_PATH
});
119 const BitTorrent::TorrentCreatorParams createTorrentParams
121 .isPrivate
= parseBool(params()[KEY_PRIVATE
]).value_or(false),
122 #ifdef QBT_USES_LIBTORRENT2
123 .torrentFormat
= parseTorrentFormat(params()[KEY_FORMAT
].toLower()),
125 .isAlignmentOptimized
= parseBool(params()[KEY_OPTIMIZE_ALIGNMENT
]).value_or(true),
126 .paddedFileSizeLimit
= parseInt(params()[KEY_PADDED_FILE_SIZE_LIMIT
]).value_or(-1),
128 .pieceSize
= parseInt(params()[KEY_PIECE_SIZE
]).value_or(0),
129 .sourcePath
= Path(params()[KEY_SOURCE_PATH
]),
130 .torrentFilePath
= Path(params()[KEY_TORRENT_FILE_PATH
]),
131 .comment
= params()[KEY_COMMENT
],
132 .source
= params()[KEY_COMMENT
],
133 .trackers
= params()[KEY_TRACKERS
].split(u
'|'),
134 .urlSeeds
= params()[KEY_URL_SEEDS
].split(u
'|')
137 bool const startSeeding
= parseBool(params()[u
"startSeeding"_s
]).value_or(createTorrentParams
.torrentFilePath
.isEmpty());
139 auto task
= m_torrentCreationManager
->createTask(createTorrentParams
, startSeeding
);
141 throw APIError(APIErrorType::Conflict
, tr("Too many active tasks"));
143 setResult(QJsonObject
{{KEY_TASK_ID
, task
->id()}});
146 void TorrentCreatorController::statusAction()
148 const QString id
= params()[KEY_TASK_ID
];
150 const auto singleTask
= m_torrentCreationManager
->getTask(id
);
151 if (!id
.isEmpty() && !singleTask
)
152 throw APIError(APIErrorType::NotFound
);
154 const QList
<std::shared_ptr
<BitTorrent::TorrentCreationTask
>> tasks
= id
.isEmpty()
155 ? m_torrentCreationManager
->tasks()
156 : QList
<std::shared_ptr
<BitTorrent::TorrentCreationTask
>> {singleTask
};
157 QJsonArray statusArray
;
158 for (const auto &task
: tasks
)
160 QJsonObject taskJson
{
161 {KEY_TASK_ID
, task
->id()},
162 {KEY_SOURCE_PATH
, task
->params().sourcePath
.toString()},
163 {KEY_PIECE_SIZE
, task
->params().pieceSize
},
164 {KEY_PRIVATE
, task
->params().isPrivate
},
165 {KEY_TIME_ADDED
, task
->timeAdded().toString()},
166 #ifdef QBT_USES_LIBTORRENT2
167 {KEY_FORMAT
, torrentFormatToString(task
->params().torrentFormat
)},
169 {KEY_OPTIMIZE_ALIGNMENT
, task
->params().isAlignmentOptimized
},
170 {KEY_PADDED_FILE_SIZE_LIMIT
, task
->params().paddedFileSizeLimit
},
172 {KEY_STATUS
, taskStatusString(task
)},
175 if (!task
->params().comment
.isEmpty())
176 taskJson
[KEY_COMMENT
] = task
->params().comment
;
178 if (!task
->params().torrentFilePath
.isEmpty())
179 taskJson
[KEY_TORRENT_FILE_PATH
] = task
->params().torrentFilePath
.toString();
181 if (!task
->params().source
.isEmpty())
182 taskJson
[KEY_SOURCE
] = task
->params().source
;
184 if (!task
->params().trackers
.isEmpty())
185 taskJson
[KEY_TRACKERS
] = QJsonArray::fromStringList(task
->params().trackers
);
187 if (!task
->params().urlSeeds
.isEmpty())
188 taskJson
[KEY_URL_SEEDS
] = QJsonArray::fromStringList(task
->params().urlSeeds
);
190 if (const QDateTime timeStarted
= task
->timeStarted(); !timeStarted
.isNull())
191 taskJson
[KEY_TIME_STARTED
] = timeStarted
.toString();
193 if (const QDateTime timeFinished
= task
->timeFinished(); !task
->timeFinished().isNull())
194 taskJson
[KEY_TIME_FINISHED
] = timeFinished
.toString();
196 if (task
->isFinished())
198 if (task
->isFailed())
200 taskJson
[KEY_ERROR_MESSAGE
] = task
->errorMsg();
204 taskJson
[KEY_PIECE_SIZE
] = task
->result().pieceSize
;
207 else if (task
->isRunning())
209 taskJson
[KEY_PROGRESS
] = task
->progress();
212 statusArray
.append(taskJson
);
215 setResult(statusArray
);
218 void TorrentCreatorController::torrentFileAction()
220 requireParams({KEY_TASK_ID
});
221 const QString id
= params()[KEY_TASK_ID
];
223 const auto task
= m_torrentCreationManager
->getTask(id
);
225 throw APIError(APIErrorType::NotFound
);
227 if (!task
->isFinished())
228 throw APIError(APIErrorType::Conflict
, tr("Torrent creation is still unfinished."));
230 if (task
->isFailed())
231 throw APIError(APIErrorType::Conflict
, tr("Torrent creation failed."));
233 const auto readResult
= Utils::IO::readFile(task
->result().torrentFilePath
, Preferences::instance()->getTorrentFileSizeLimit());
235 throw APIError(APIErrorType::Conflict
, readResult
.error().message
);
237 setResult(readResult
.value(), u
"application/x-bittorrent"_s
, (id
+ u
".torrent"));
240 void TorrentCreatorController::deleteTaskAction()
242 requireParams({KEY_TASK_ID
});
243 const QString id
= params()[KEY_TASK_ID
];
245 if (!m_torrentCreationManager
->deleteTask(id
))
246 throw APIError(APIErrorType::NotFound
);