qBittorrent
torrentcreatorthread.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * In addition, as a special exception, the copyright holders give permission to
20  * link this program with the OpenSSL project's "OpenSSL" library (or with
21  * modified versions of it that use the same license as the "OpenSSL" library),
22  * and distribute the linked executables. You must obey the GNU General Public
23  * License in all respects for all of the code used other than "OpenSSL". If you
24  * modify file(s), you may extend this exception to your version of the file(s),
25  * but you are not obligated to do so. If you do not wish to do so, delete this
26  * exception statement from your version.
27  */
28 
29 #include "torrentcreatorthread.h"
30 
31 #include <fstream>
32 
33 #include <libtorrent/create_torrent.hpp>
34 #include <libtorrent/file_storage.hpp>
35 #include <libtorrent/torrent_info.hpp>
36 
37 #include <QDirIterator>
38 #include <QFileInfo>
39 #include <QHash>
40 
41 #include "base/exceptions.h"
42 #include "base/global.h"
43 #include "base/utils/compare.h"
44 #include "base/utils/fs.h"
45 #include "base/utils/io.h"
46 #include "base/version.h"
47 #include "lttypecast.h"
48 
49 namespace
50 {
51  // do not include files and folders whose
52  // name starts with a .
53  bool fileFilter(const std::string &f)
54  {
55  return !Utils::Fs::fileName(QString::fromStdString(f)).startsWith('.');
56  }
57 
58 #ifdef QBT_USES_LIBTORRENT2
59  lt::create_flags_t toNativeTorrentFormatFlag(const BitTorrent::TorrentFormat torrentFormat)
60  {
61  switch (torrentFormat)
62  {
63  case BitTorrent::TorrentFormat::V1:
64  return lt::create_torrent::v1_only;
65  case BitTorrent::TorrentFormat::Hybrid:
66  return {};
67  case BitTorrent::TorrentFormat::V2:
68  return lt::create_torrent::v2_only;
69  }
70  return {};
71  }
72 #endif
73 }
74 
75 using namespace BitTorrent;
76 
78  : QThread(parent)
79 {
80 }
81 
83 {
84  requestInterruption();
85  wait();
86 }
87 
89 {
90  m_params = params;
91  start();
92 }
93 
94 void TorrentCreatorThread::sendProgressSignal(int currentPieceIdx, int totalPieces)
95 {
96  emit updateProgress(static_cast<int>((currentPieceIdx * 100.) / totalPieces));
97 }
98 
100 {
101  if (isInterruptionRequested())
102  throw RuntimeError(tr("Operation aborted"));
103 }
104 
106 {
107  emit updateProgress(0);
108 
109  try
110  {
111  const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + '/';
113 
114  // Adding files to the torrent
115  lt::file_storage fs;
116  if (QFileInfo(m_params.inputPath).isFile())
117  {
118  lt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
119  }
120  else
121  {
122  // need to sort the file names by natural sort order
123  QStringList dirs = {m_params.inputPath};
124 
125  QDirIterator dirIter {m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories};
126  while (dirIter.hasNext())
127  {
128  dirIter.next();
129  dirs += dirIter.filePath();
130  }
131  std::sort(dirs.begin(), dirs.end(), naturalLessThan);
132 
133  QStringList fileNames;
134  QHash<QString, qint64> fileSizeMap;
135 
136  for (const auto &dir : asConst(dirs))
137  {
138  QStringList tmpNames; // natural sort files within each dir
139 
140  QDirIterator fileIter(dir, QDir::Files);
141  while (fileIter.hasNext())
142  {
143  fileIter.next();
144 
145  const QString relFilePath = fileIter.filePath().mid(parentPath.length());
146  tmpNames += relFilePath;
147  fileSizeMap[relFilePath] = fileIter.fileInfo().size();
148  }
149 
150  std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan);
151  fileNames += tmpNames;
152  }
153 
154  for (const auto &fileName : asConst(fileNames))
155  fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
156  }
157 
159 
160 #ifdef QBT_USES_LIBTORRENT2
161  lt::create_torrent newTorrent {fs, m_params.pieceSize, toNativeTorrentFormatFlag(m_params.torrentFormat)};
162 #else
163  lt::create_torrent newTorrent(fs, m_params.pieceSize, m_params.paddedFileSizeLimit
164  , (m_params.isAlignmentOptimized ? lt::create_torrent::optimize_alignment : lt::create_flags_t {}));
165 #endif
166 
167  // Add url seeds
168  for (QString seed : asConst(m_params.urlSeeds))
169  {
170  seed = seed.trimmed();
171  if (!seed.isEmpty())
172  newTorrent.add_url_seed(seed.toStdString());
173  }
174 
175  int tier = 0;
176  for (const QString &tracker : asConst(m_params.trackers))
177  {
178  if (tracker.isEmpty())
179  ++tier;
180  else
181  newTorrent.add_tracker(tracker.trimmed().toStdString(), tier);
182  }
183 
184  // calculate the hash for all pieces
185  lt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
186  , [this, &newTorrent](const lt::piece_index_t n)
187  {
189  sendProgressSignal(LT::toUnderlyingType(n), newTorrent.num_pieces());
190  });
191 
192  // Set qBittorrent as creator and add user comment to
193  // torrent_info structure
194  newTorrent.set_creator("qBittorrent " QBT_VERSION);
195  newTorrent.set_comment(m_params.comment.toUtf8().constData());
196  // Is private ?
197  newTorrent.set_priv(m_params.isPrivate);
198 
200 
201  lt::entry entry = newTorrent.generate();
202 
203  // add source field
204  if (!m_params.source.isEmpty())
205  entry["info"]["source"] = m_params.source.toStdString();
206 
208 
209  // create the torrent
210  const nonstd::expected<void, QString> result = Utils::IO::saveToFile(m_params.savePath, entry);
211  if (!result)
212  throw RuntimeError(result.error());
213 
214  emit updateProgress(100);
215  emit creationSuccess(m_params.savePath, parentPath);
216  }
217  catch (const RuntimeError &err)
218  {
219  emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(err.message()));
220  }
221  catch (const std::exception &err)
222  {
223  emit creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(QString::fromLocal8Bit(err.what())));
224  }
225 }
226 
227 #ifdef QBT_USES_LIBTORRENT2
228 int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const TorrentFormat torrentFormat)
229 #else
230 int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized, const int paddedFileSizeLimit)
231 #endif
232 {
233  if (inputPath.isEmpty())
234  return 0;
235 
236  lt::file_storage fs;
237  lt::add_files(fs, Utils::Fs::toNativePath(inputPath).toStdString(), fileFilter);
238 
239 #ifdef QBT_USES_LIBTORRENT2
240  return lt::create_torrent {fs, pieceSize, toNativeTorrentFormatFlag(torrentFormat)}.num_pieces();
241 #else
242  return lt::create_torrent(fs, pieceSize, paddedFileSizeLimit
243  , (isAlignmentOptimized ? lt::create_torrent::optimize_alignment : lt::create_flags_t {})).num_pieces();
244 #endif
245 }
static int calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized, int paddedFileSizeLimit)
TorrentCreatorThread(QObject *parent=nullptr)
void creationSuccess(const QString &path, const QString &branchPath)
void updateProgress(int progress)
void create(const TorrentCreatorParams &params)
void sendProgressSignal(int currentPieceIdx, int totalPieces)
void creationFailure(const QString &msg)
QString message() const noexcept
Definition: exceptions.cpp:36
constexpr std::add_const_t< T > & asConst(T &t) noexcept
Definition: global.h:42
constexpr T::underlying_type toUnderlyingType(const T &t) noexcept
Definition: lttypecast.h:38
QString fileName(const QString &filePath)
Definition: fs.cpp:87
QString branchPath(const QString &filePath, QString *removed=nullptr)
Definition: fs.cpp:276
QString toNativePath(const QString &path)
Definition: fs.cpp:64
nonstd::expected< void, QString > saveToFile(const QString &path, const QByteArray &data)
Definition: io.cpp:69
void f()
Definition: test2.c:1