2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
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.
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.
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.
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.
29 #include "torrentcreatorthread.h"
33 #include <libtorrent/create_torrent.hpp>
34 #include <libtorrent/file_storage.hpp>
35 #include <libtorrent/torrent_info.hpp>
37 #include <QDirIterator>
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"
51 // do not include files and folders whose
52 // name starts with a .
53 bool fileFilter(const std::string
&f
)
55 return !Path(f
).filename().startsWith(u
'.');
58 #ifdef QBT_USES_LIBTORRENT2
59 lt::create_flags_t
toNativeTorrentFormatFlag(const BitTorrent::TorrentFormat torrentFormat
)
61 switch (torrentFormat
)
63 case BitTorrent::TorrentFormat::V1
:
64 return lt::create_torrent::v1_only
;
65 case BitTorrent::TorrentFormat::Hybrid
:
67 case BitTorrent::TorrentFormat::V2
:
68 return lt::create_torrent::v2_only
;
75 using namespace BitTorrent
;
77 TorrentCreatorThread::TorrentCreatorThread(QObject
*parent
)
82 TorrentCreatorThread::~TorrentCreatorThread()
84 requestInterruption();
88 void TorrentCreatorThread::create(const TorrentCreatorParams
¶ms
)
94 void TorrentCreatorThread::sendProgressSignal(int currentPieceIdx
, int totalPieces
)
96 emit
updateProgress(static_cast<int>((currentPieceIdx
* 100.) / totalPieces
));
99 void TorrentCreatorThread::checkInterruptionRequested() const
101 if (isInterruptionRequested())
102 throw RuntimeError(tr("Operation aborted"));
105 void TorrentCreatorThread::run()
107 emit
updateProgress(0);
111 const Path parentPath
= m_params
.inputPath
.parentPath();
112 const Utils::Compare::NaturalLessThan
<Qt::CaseInsensitive
> naturalLessThan
{};
114 // Adding files to the torrent
116 if (QFileInfo(m_params
.inputPath
.data()).isFile())
118 lt::add_files(fs
, m_params
.inputPath
.toString().toStdString(), fileFilter
);
122 // need to sort the file names by natural sort order
123 QStringList dirs
= {m_params
.inputPath
.data()};
125 QDirIterator dirIter
{m_params
.inputPath
.data(), (QDir::AllDirs
| QDir::NoDotAndDotDot
), QDirIterator::Subdirectories
};
126 while (dirIter
.hasNext())
129 dirs
.append(dirIter
.filePath());
131 std::sort(dirs
.begin(), dirs
.end(), naturalLessThan
);
133 QStringList fileNames
;
134 QHash
<QString
, qint64
> fileSizeMap
;
136 for (const QString
&dir
: asConst(dirs
))
138 QStringList tmpNames
; // natural sort files within each dir
140 QDirIterator fileIter
{dir
, QDir::Files
};
141 while (fileIter
.hasNext())
145 const auto relFilePath
= parentPath
.relativePathOf(Path(fileIter
.filePath()));
146 tmpNames
.append(relFilePath
.toString());
147 fileSizeMap
[tmpNames
.last()] = fileIter
.fileInfo().size();
150 std::sort(tmpNames
.begin(), tmpNames
.end(), naturalLessThan
);
151 fileNames
+= tmpNames
;
154 for (const QString
&fileName
: asConst(fileNames
))
155 fs
.add_file(fileName
.toStdString(), fileSizeMap
[fileName
]);
158 checkInterruptionRequested();
160 #ifdef QBT_USES_LIBTORRENT2
161 lt::create_torrent newTorrent
{fs
, m_params
.pieceSize
, toNativeTorrentFormatFlag(m_params
.torrentFormat
)};
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
{}));
168 for (QString seed
: asConst(m_params
.urlSeeds
))
170 seed
= seed
.trimmed();
172 newTorrent
.add_url_seed(seed
.toStdString());
176 for (const QString
&tracker
: asConst(m_params
.trackers
))
178 if (tracker
.isEmpty())
181 newTorrent
.add_tracker(tracker
.trimmed().toStdString(), tier
);
184 // calculate the hash for all pieces
185 lt::set_piece_hashes(newTorrent
, parentPath
.toString().toStdString()
186 , [this, &newTorrent
](const lt::piece_index_t n
)
188 checkInterruptionRequested();
189 sendProgressSignal(LT::toUnderlyingType(n
), newTorrent
.num_pieces());
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());
197 newTorrent
.set_priv(m_params
.isPrivate
);
199 checkInterruptionRequested();
201 lt::entry entry
= newTorrent
.generate();
204 if (!m_params
.source
.isEmpty())
205 entry
["info"]["source"] = m_params
.source
.toStdString();
207 checkInterruptionRequested();
209 // create the torrent
210 const nonstd::expected
<void, QString
> result
= Utils::IO::saveToFile(m_params
.savePath
, entry
);
212 throw RuntimeError(result
.error());
214 emit
updateProgress(100);
215 emit
creationSuccess(m_params
.savePath
, parentPath
);
217 catch (const RuntimeError
&err
)
219 emit
creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(err
.message()));
221 catch (const std::exception
&err
)
223 emit
creationFailure(tr("Create new torrent file failed. Reason: %1.").arg(QString::fromLocal8Bit(err
.what())));
227 #ifdef QBT_USES_LIBTORRENT2
228 int TorrentCreatorThread::calculateTotalPieces(const Path
&inputPath
, const int pieceSize
, const TorrentFormat torrentFormat
)
230 int TorrentCreatorThread::calculateTotalPieces(const Path
&inputPath
, const int pieceSize
, const bool isAlignmentOptimized
, const int paddedFileSizeLimit
)
233 if (inputPath
.isEmpty())
237 lt::add_files(fs
, inputPath
.toString().toStdString(), fileFilter
);
239 #ifdef QBT_USES_LIBTORRENT2
240 return lt::create_torrent
{fs
, pieceSize
, toNativeTorrentFormatFlag(torrentFormat
)}.num_pieces();
242 return lt::create_torrent(fs
, pieceSize
, paddedFileSizeLimit
243 , (isAlignmentOptimized
? lt::create_torrent::optimize_alignment
: lt::create_flags_t
{})).num_pieces();