qBittorrent
torrentcreatordialog.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2017 Mike Tzou (Chocobo1)
4  * Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
5  *
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.
10  *
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.
15  *
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.
19  *
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.
28  */
29 
30 #include "torrentcreatordialog.h"
31 
32 #include <QCloseEvent>
33 #include <QFileDialog>
34 #include <QMessageBox>
35 #include <QMimeData>
36 #include <QUrl>
37 
40 #include "base/global.h"
41 #include "base/utils/fs.h"
42 #include "ui_torrentcreatordialog.h"
43 #include "utils.h"
44 
45 #define SETTINGS_KEY(name) "TorrentCreator/" name
46 
47 TorrentCreatorDialog::TorrentCreatorDialog(QWidget *parent, const QString &defaultPath)
48  : QDialog(parent)
49  , m_ui(new Ui::TorrentCreatorDialog)
50  , m_creatorThread(new BitTorrent::TorrentCreatorThread(this))
51  , m_storeDialogSize(SETTINGS_KEY("Size"))
52  , m_storePieceSize(SETTINGS_KEY("PieceSize"))
53  , m_storePrivateTorrent(SETTINGS_KEY("PrivateTorrent"))
54  , m_storeStartSeeding(SETTINGS_KEY("StartSeeding"))
55  , m_storeIgnoreRatio(SETTINGS_KEY("IgnoreRatio"))
56 #ifdef QBT_USES_LIBTORRENT2
57  , m_storeTorrentFormat(SETTINGS_KEY("TorrentFormat"))
58 #else
59  , m_storeOptimizeAlignment(SETTINGS_KEY("OptimizeAlignment"))
60  , m_paddedFileSizeLimit(SETTINGS_KEY("PaddedFileSizeLimit"))
61 #endif
62  , m_storeLastAddPath(SETTINGS_KEY("LastAddPath"))
63  , m_storeTrackerList(SETTINGS_KEY("TrackerList"))
64  , m_storeWebSeedList(SETTINGS_KEY("WebSeedList"))
65  , m_storeComments(SETTINGS_KEY("Comments"))
66  , m_storeLastSavePath(SETTINGS_KEY("LastSavePath"))
67  , m_storeSource(SETTINGS_KEY("Source"))
68 {
69  m_ui->setupUi(this);
70  setAttribute(Qt::WA_DeleteOnClose);
71  setModal(false);
72 
73  m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Create Torrent"));
74 
75  connect(m_ui->addFileButton, &QPushButton::clicked, this, &TorrentCreatorDialog::onAddFileButtonClicked);
76  connect(m_ui->addFolderButton, &QPushButton::clicked, this, &TorrentCreatorDialog::onAddFolderButtonClicked);
77  connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &TorrentCreatorDialog::onCreateButtonClicked);
78  connect(m_ui->buttonCalcTotalPieces, &QPushButton::clicked, this, &TorrentCreatorDialog::updatePiecesCount);
79 
83 
84  loadSettings();
85  updateInputPath(defaultPath);
86 
87 #ifdef QBT_USES_LIBTORRENT2
88  m_ui->checkOptimizeAlignment->hide();
89 #else
90  m_ui->widgetTorrentFormat->hide();
91 #endif
92 
93  show();
94 }
95 
97 {
98  saveSettings();
99 
100  delete m_ui;
101 }
102 
103 void TorrentCreatorDialog::updateInputPath(const QString &path)
104 {
105  if (path.isEmpty()) return;
106  m_ui->textInputPath->setText(Utils::Fs::toNativePath(path));
108 }
109 
111 {
112  QString oldPath = m_ui->textInputPath->text();
113  QString path = QFileDialog::getExistingDirectory(this, tr("Select folder"), oldPath);
114  updateInputPath(path);
115 }
116 
118 {
119  QString oldPath = m_ui->textInputPath->text();
120  QString path = QFileDialog::getOpenFileName(this, tr("Select file"), oldPath);
121  updateInputPath(path);
122 }
123 
125 {
126  const int pieceSizes[] = {0, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768}; // base unit in KiB
127  return pieceSizes[m_ui->comboPieceSize->currentIndex()] * 1024;
128 }
129 
130 #ifdef QBT_USES_LIBTORRENT2
131 BitTorrent::TorrentFormat TorrentCreatorDialog::getTorrentFormat() const
132 {
133  switch (m_ui->comboTorrentFormat->currentIndex())
134  {
135  case 0:
136  return BitTorrent::TorrentFormat::V2;
137  case 1:
138  return BitTorrent::TorrentFormat::Hybrid;
139  case 2:
140  return BitTorrent::TorrentFormat::V1;
141  }
142  return BitTorrent::TorrentFormat::Hybrid;
143 }
144 #else
146 {
147  const int value = m_ui->spinPaddedFileSizeLimit->value();
148  return ((value >= 0) ? (value * 1024) : -1);
149 }
150 #endif
151 
152 void TorrentCreatorDialog::dropEvent(QDropEvent *event)
153 {
154  event->acceptProposedAction();
155 
156  if (event->mimeData()->hasUrls())
157  {
158  // only take the first one
159  QUrl firstItem = event->mimeData()->urls().first();
160  QString path = (firstItem.scheme().compare("file", Qt::CaseInsensitive) == 0)
161  ? firstItem.toLocalFile() : firstItem.toString();
162  updateInputPath(path);
163  }
164 }
165 
166 void TorrentCreatorDialog::dragEnterEvent(QDragEnterEvent *event)
167 {
168  if (event->mimeData()->hasFormat("text/plain") || event->mimeData()->hasFormat("text/uri-list"))
169  event->acceptProposedAction();
170 }
171 
172 // Main function that create a .torrent file
174 {
175  QString input = Utils::Fs::toUniformPath(m_ui->textInputPath->text()).trimmed();
176 
177  // test if readable
178  const QFileInfo fi(input);
179  if (!fi.isReadable())
180  {
181  QMessageBox::critical(this, tr("Torrent creation failed"), tr("Reason: Path to file/folder is not readable."));
182  return;
183  }
184  input = fi.canonicalFilePath();
185 
186  // get save path
187  const QString savePath = m_storeLastSavePath.get(QDir::homePath()) + QLatin1Char('/') + fi.fileName() + QLatin1String(".torrent");
188  QString destination = QFileDialog::getSaveFileName(this, tr("Select where to save the new torrent"), savePath, tr("Torrent Files (*.torrent)"));
189  if (destination.isEmpty())
190  return;
191  if (!destination.endsWith(C_TORRENT_FILE_EXTENSION, Qt::CaseInsensitive))
192  destination += C_TORRENT_FILE_EXTENSION;
194 
195  // Disable dialog & set busy cursor
196  setInteractionEnabled(false);
197  setCursor(QCursor(Qt::WaitCursor));
198 
199  const QStringList trackers = m_ui->trackersList->toPlainText().trimmed()
200  .replace(QRegularExpression("\n\n[\n]+"), "\n\n").split('\n');
202  {
203  m_ui->checkPrivate->isChecked()
204 #ifdef QBT_USES_LIBTORRENT2
205  , getTorrentFormat()
206 #else
207  , m_ui->checkOptimizeAlignment->isChecked()
209 #endif
210  , getPieceSize()
211  , input, destination
212  , m_ui->txtComment->toPlainText()
213  , m_ui->lineEditSource->text()
214  , trackers
215  , m_ui->URLSeedsList->toPlainText().split('\n', Qt::SkipEmptyParts)
216  };
217 
218  // run the creator thread
219  m_creatorThread->create(params);
220 }
221 
223 {
224  // Remove busy cursor
225  setCursor(QCursor(Qt::ArrowCursor));
226  QMessageBox::information(this, tr("Torrent creation failed"), tr("Reason: %1").arg(msg));
227  setInteractionEnabled(true);
228 }
229 
230 void TorrentCreatorDialog::handleCreationSuccess(const QString &path, const QString &branchPath)
231 {
232  // Remove busy cursor
233  setCursor(QCursor(Qt::ArrowCursor));
234  if (m_ui->checkStartSeeding->isChecked())
235  {
236  // Create save path temp data
237  const nonstd::expected<BitTorrent::TorrentInfo, QString> result = BitTorrent::TorrentInfo::loadFromFile(Utils::Fs::toNativePath(path));
238  if (!result)
239  {
240  QMessageBox::critical(this, tr("Torrent creation failed"), tr("Reason: Created torrent is invalid. It won't be added to download list."));
241  return;
242  }
243 
245  params.savePath = branchPath;
246  params.skipChecking = true;
247  if (m_ui->checkIgnoreShareLimits->isChecked())
248  {
251  }
252  params.useAutoTMM = false; // otherwise if it is on by default, it will overwrite `savePath` to the default save path
253 
254  BitTorrent::Session::instance()->addTorrent(result.value(), params);
255  }
256  QMessageBox::information(this, tr("Torrent creator")
257  , QString::fromLatin1("%1\n%2").arg(tr("Torrent created:"), Utils::Fs::toNativePath(path)));
258  setInteractionEnabled(true);
259 }
260 
262 {
263  m_ui->progressBar->setValue(progress);
264 }
265 
267 {
268  const QString path = m_ui->textInputPath->text().trimmed();
269 #ifdef QBT_USES_LIBTORRENT2
271  path, getPieceSize(), getTorrentFormat());
272 #else
273  const bool isAlignmentOptimized = m_ui->checkOptimizeAlignment->isChecked();
275  , getPieceSize(), isAlignmentOptimized, getPaddedFileSizeLimit());
276 #endif
277  m_ui->labelTotalPieces->setText(QString::number(count));
278 }
279 
280 void TorrentCreatorDialog::setInteractionEnabled(const bool enabled) const
281 {
282  m_ui->textInputPath->setEnabled(enabled);
283  m_ui->addFileButton->setEnabled(enabled);
284  m_ui->addFolderButton->setEnabled(enabled);
285  m_ui->trackersList->setEnabled(enabled);
286  m_ui->URLSeedsList->setEnabled(enabled);
287  m_ui->txtComment->setEnabled(enabled);
288  m_ui->comboPieceSize->setEnabled(enabled);
289  m_ui->buttonCalcTotalPieces->setEnabled(enabled);
290  m_ui->checkPrivate->setEnabled(enabled);
291  m_ui->checkStartSeeding->setEnabled(enabled);
292  m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled);
293  m_ui->checkIgnoreShareLimits->setEnabled(enabled && m_ui->checkStartSeeding->isChecked());
294 #ifdef QBT_USES_LIBTORRENT2
295  m_ui->widgetTorrentFormat->setEnabled(enabled);
296 #else
297  m_ui->checkOptimizeAlignment->setEnabled(enabled);
298  m_ui->spinPaddedFileSizeLimit->setEnabled(enabled);
299 #endif
300 }
301 
303 {
304  m_storeLastAddPath = m_ui->textInputPath->text().trimmed();
305 
306  m_storePieceSize = m_ui->comboPieceSize->currentIndex();
307  m_storePrivateTorrent = m_ui->checkPrivate->isChecked();
308  m_storeStartSeeding = m_ui->checkStartSeeding->isChecked();
309  m_storeIgnoreRatio = m_ui->checkIgnoreShareLimits->isChecked();
310 #ifdef QBT_USES_LIBTORRENT2
311  m_storeTorrentFormat = m_ui->comboTorrentFormat->currentIndex();
312 #else
313  m_storeOptimizeAlignment = m_ui->checkOptimizeAlignment->isChecked();
314  m_paddedFileSizeLimit = m_ui->spinPaddedFileSizeLimit->value();
315 #endif
316 
317  m_storeTrackerList = m_ui->trackersList->toPlainText();
318  m_storeWebSeedList = m_ui->URLSeedsList->toPlainText();
319  m_storeComments = m_ui->txtComment->toPlainText();
320  m_storeSource = m_ui->lineEditSource->text();
321 
322  m_storeDialogSize = size();
323 }
324 
326 {
327  m_ui->textInputPath->setText(m_storeLastAddPath.get(QDir::homePath()));
328 
329  m_ui->comboPieceSize->setCurrentIndex(m_storePieceSize);
330  m_ui->checkPrivate->setChecked(m_storePrivateTorrent);
331  m_ui->checkStartSeeding->setChecked(m_storeStartSeeding);
332  m_ui->checkIgnoreShareLimits->setChecked(m_storeIgnoreRatio);
333  m_ui->checkIgnoreShareLimits->setEnabled(m_ui->checkStartSeeding->isChecked());
334 #ifdef QBT_USES_LIBTORRENT2
335  m_ui->comboTorrentFormat->setCurrentIndex(m_storeTorrentFormat.get(1));
336 #else
337  m_ui->checkOptimizeAlignment->setChecked(m_storeOptimizeAlignment.get(true));
338  m_ui->spinPaddedFileSizeLimit->setValue(m_paddedFileSizeLimit.get(-1));
339 #endif
340 
341  m_ui->trackersList->setPlainText(m_storeTrackerList);
342  m_ui->URLSeedsList->setPlainText(m_storeWebSeedList);
343  m_ui->txtComment->setPlainText(m_storeComments);
344  m_ui->lineEditSource->setText(m_storeSource);
345 
347 }
static Session * instance()
Definition: session.cpp:997
bool addTorrent(const QString &source, const AddTorrentParams &params=AddTorrentParams())
Definition: session.cpp:2007
static int calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit)
void creationSuccess(const QString &path, const QString &branchPath)
void updateProgress(int progress)
void create(const TorrentCreatorParams &params)
void creationFailure(const QString &msg)
static const int NO_SEEDING_TIME_LIMIT
Definition: torrent.h:108
static const qreal NO_RATIO_LIMIT
Definition: torrent.h:105
static nonstd::expected< TorrentInfo, QString > loadFromFile(const QString &path) noexcept
T get(const T &defaultValue={}) const
Definition: settingvalue.h:46
SettingValue< QString > m_storeComments
SettingValue< QString > m_storeWebSeedList
void updateInputPath(const QString &path)
SettingValue< bool > m_storeIgnoreRatio
void updateProgressBar(int progress)
TorrentCreatorDialog(QWidget *parent=nullptr, const QString &defaultPath={})
SettingValue< QString > m_storeSource
void dropEvent(QDropEvent *event) override
SettingValue< QString > m_storeTrackerList
SettingValue< bool > m_storeOptimizeAlignment
void dragEnterEvent(QDragEnterEvent *event) override
SettingValue< QString > m_storeLastSavePath
SettingValue< bool > m_storeStartSeeding
Ui::TorrentCreatorDialog * m_ui
BitTorrent::TorrentCreatorThread * m_creatorThread
SettingValue< QString > m_storeLastAddPath
SettingValue< QSize > m_storeDialogSize
void setInteractionEnabled(bool enabled) const
void handleCreationSuccess(const QString &path, const QString &branchPath)
SettingValue< int > m_storePieceSize
SettingValue< bool > m_storePrivateTorrent
SettingValue< int > m_paddedFileSizeLimit
void handleCreationFailure(const QString &msg)
const char C_TORRENT_FILE_EXTENSION[]
Definition: global.h:38
QString toUniformPath(const QString &path)
Definition: fs.cpp:69
QString branchPath(const QString &filePath, QString *removed=nullptr)
Definition: fs.cpp:276
QString toNativePath(const QString &path)
Definition: fs.cpp:64
void resize(QWidget *widget, const QSize &newSize={})
Definition: utils.cpp:54
T value(const QString &key, const T &defaultValue={})
Definition: preferences.cpp:64
std::optional< bool > useAutoTMM
#define SETTINGS_KEY(name)