File indexing completed on 2024-05-05 09:11:19
0001 /* This file is part of the KDE project 0002 0003 Copyright (C) 2007-2008 Lukas Appelhans <l.appelhans@gmx.de> 0004 Copyright (C) 2007 Joris Guisson <joris.guisson@gmail.com> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 */ 0011 0012 #include "bttransfer.h" 0013 #include "bittorrentsettings.h" 0014 #include "bttransferhandler.h" 0015 // #include "btchunkselector.h" 0016 #include "advanceddetails/monitor.h" 0017 #include "core/download.h" 0018 #include "core/filemodel.h" 0019 #include "core/kget.h" 0020 #include "kget_version.h" 0021 0022 #include <interfaces/trackerinterface.h> 0023 #include <peer/authenticationmonitor.h> 0024 #include <peer/peermanager.h> 0025 #include <torrent/globals.h> 0026 #include <torrent/server.h> 0027 #include <torrent/torrent.h> 0028 #include <util/constants.h> 0029 #include <util/error.h> 0030 #include <util/functions.h> 0031 #include <util/log.h> 0032 #include <utp/utpserver.h> 0033 #include <version.h> 0034 0035 #include "kget_debug.h" 0036 #include <KIO/CopyJob> 0037 #include <KLocalizedString> 0038 0039 #include <KMessageBox> 0040 0041 #include <QDir> 0042 #include <QDomElement> 0043 #include <QFile> 0044 #include <QFileDialog> 0045 #include <QFileInfo> 0046 #include <QStandardPaths> 0047 #include <QUrl> 0048 0049 #ifdef ERROR 0050 #undef ERROR 0051 #endif 0052 0053 BTTransfer::BTTransfer(TransferGroup *parent, TransferFactory *factory, Scheduler *scheduler, const QUrl &src, const QUrl &dest, const QDomElement *e) 0054 : Transfer(parent, factory, scheduler, src, dest, e) 0055 , torrent(nullptr) 0056 , m_tmp(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/tmp/")) 0057 , m_ready(false) 0058 , m_downloadFinished(false) 0059 , m_movingFile(false) 0060 , m_fileModel(nullptr) 0061 , m_updateCounter(0) 0062 { 0063 QString tmpDirName = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/tmp/"); 0064 // make sure that the /tmp directory exists (earlier this used to be handled by KStandardDirs) 0065 if (!QFileInfo::exists(tmpDirName)) { 0066 QDir().mkpath(tmpDirName); 0067 } 0068 m_directory = KIO::upUrl(m_dest); // FIXME test 0069 0070 setCapabilities(Transfer::Cap_Moving | Transfer::Cap_Renaming | Transfer::Cap_Resuming | Transfer::Cap_SpeedLimit); 0071 } 0072 0073 BTTransfer::~BTTransfer() 0074 { 0075 if (torrent && m_ready) 0076 torrent->setMonitor(nullptr); 0077 0078 delete torrent; 0079 } 0080 0081 void BTTransfer::deinit(Transfer::DeleteOptions options) 0082 { 0083 qDebug() << "****************************DEINIT"; 0084 if (torrent && (options & Transfer::DeleteFiles)) { // FIXME: Also delete when torrent does not exist 0085 torrent->deleteDataFiles(); 0086 } 0087 if (options & Transfer::DeleteTemporaryFiles) { 0088 QDir tmpDir(m_tmp); 0089 qCDebug(KGET_DEBUG) << m_tmp + m_source.fileName().remove(".torrent"); 0090 tmpDir.rmdir(m_source.fileName().remove(".torrent") + "/dnd"); 0091 tmpDir.cd(m_source.fileName().remove(".torrent")); 0092 QStringList list = tmpDir.entryList(); 0093 foreach (const QString &file, list) { 0094 tmpDir.remove(file); 0095 } 0096 tmpDir.cdUp(); 0097 tmpDir.rmdir(m_source.fileName().remove(".torrent")); 0098 0099 // only remove the .torrent file if it was downloaded by KGet 0100 if (!m_tmpTorrentFile.isEmpty()) { 0101 qCDebug(KGET_DEBUG) << "Removing" << m_tmpTorrentFile; 0102 QFile torrentFile(m_tmpTorrentFile); 0103 torrentFile.remove(); 0104 } 0105 } 0106 } 0107 0108 /** Reimplemented functions from Transfer-Class **/ 0109 bool BTTransfer::isStalled() const 0110 { 0111 return (status() == Job::Running) && (downloadSpeed() == 0) && torrent && torrent->getStats().status == bt::STALLED; 0112 } 0113 0114 bool BTTransfer::isWorking() const 0115 { 0116 if (!torrent) 0117 return false; 0118 const bt::TorrentStats stats = torrent->getStats(); 0119 return (stats.status != bt::ERROR) && (stats.status != bt::STALLED) && (stats.status != bt::NO_SPACE_LEFT) && (stats.status != bt::INVALID_STATUS); 0120 } 0121 0122 void BTTransfer::resolveError(int errorId) 0123 { 0124 switch (errorId) { 0125 case TorrentFileNotFoundError: { 0126 QFileDialog *dlg = new QFileDialog(nullptr, i18nc("@title", "Select a New Torrent File")); 0127 dlg->setFileMode(QFileDialog::ExistingFile); 0128 dlg->setMimeTypeFilters(QStringList{QStringLiteral("application/x-bittorrent")}); 0129 dlg->setAttribute(Qt::WA_DeleteOnClose); 0130 connect(dlg, &QDialog::accepted, this, [this, dlg] { 0131 QUrl url = dlg->selectedUrls().value(0); 0132 if (url.isValid()) { 0133 btTransferInit(url); 0134 } 0135 }); 0136 dlg->show(); 0137 0138 break; 0139 } 0140 default: 0141 return; 0142 } 0143 } 0144 0145 void BTTransfer::start() 0146 { 0147 if (m_movingFile) { 0148 return; 0149 } 0150 0151 if (!torrent) { 0152 if (!m_source.isLocalFile()) { 0153 qCDebug(KGET_DEBUG) << m_dest.path(); 0154 QString tmpDirName = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/tmp/"); 0155 m_tmpTorrentFile = tmpDirName + m_dest.fileName(); 0156 auto *download = new Download(m_source, QUrl::fromLocalFile(m_tmpTorrentFile)); 0157 0158 setStatus(Job::Stopped, i18n("Downloading Torrent File...."), "document-save"); 0159 setTransferChange(Tc_Status, true); 0160 0161 // m_source = tmpDirName + m_source.fileName(); 0162 connect(download, &Download::finishedSuccessfully, this, &BTTransfer::btTransferInit); 0163 } else 0164 btTransferInit(); 0165 } else 0166 startTorrent(); 0167 } 0168 0169 bool BTTransfer::setDirectory(const QUrl &newDirectory) 0170 { 0171 // check if the newDestination is the same as the old 0172 QUrl temp = newDirectory; 0173 temp = temp.adjusted(QUrl::StripTrailingSlash); 0174 temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name)); 0175 if (newDirectory.isValid() && (newDirectory != dest()) && (temp != dest())) { 0176 if (torrent->changeOutputDir(newDirectory.url(QUrl::PreferLocalFile), bt::TorrentInterface::MOVE_FILES)) { 0177 connect(torrent, &bt::TorrentInterface::aboutToBeStarted, this, &BTTransfer::newDestResult); 0178 m_movingFile = true; 0179 m_directory = newDirectory; 0180 m_dest = m_directory; 0181 m_dest = m_dest.adjusted(QUrl::StripTrailingSlash); 0182 m_dest.setPath(m_dest.path() + '/' + (torrent->getStats().torrent_name)); 0183 0184 setStatus(Job::Stopped, i18nc("changing the destination of the file", "Changing destination"), "media-playback-pause"); 0185 setTransferChange(Tc_Status, true); 0186 return true; 0187 } 0188 } 0189 m_movingFile = false; 0190 return false; 0191 } 0192 0193 void BTTransfer::newDestResult() 0194 { 0195 disconnect(torrent, &bt::TorrentInterface::aboutToBeStarted, this, &BTTransfer::newDestResult); 0196 m_movingFile = false; 0197 0198 setStatus(Job::Running, i18nc("transfer state: downloading", "Downloading...."), "media-playback-start"); 0199 setTransferChange(Tc_FileName | Tc_Status, true); 0200 } 0201 0202 void BTTransfer::stop() 0203 { 0204 if (m_movingFile) 0205 return; 0206 0207 if (m_ready) { 0208 stopTorrent(); 0209 } 0210 } 0211 0212 /**Own public functions**/ 0213 void BTTransfer::update() 0214 { 0215 if (m_movingFile) { 0216 return; 0217 } 0218 0219 if (torrent) { 0220 QStringList files; 0221 if (torrent->hasMissingFiles(files)) { 0222 torrent->recreateMissingFiles(); 0223 } 0224 updateTorrent(); 0225 } else 0226 timer.stop(); 0227 } 0228 0229 void BTTransfer::load(const QDomElement *element) 0230 { 0231 Transfer::load(element); 0232 0233 if ((m_totalSize == m_downloadedSize) && (m_totalSize != 0)) { 0234 setStatus(Job::Stopped, i18nc("transfer state: finished", "Finished"), "dialog-ok"); 0235 } 0236 } 0237 0238 // void BTTransfer::save(const QDomElement &element) 0239 // { 0240 // qCDebug(KGET_DEBUG); 0241 // 0242 // QDomElement e = element; 0243 // 0244 // Transfer::save(e); 0245 // } 0246 0247 /**Public functions of BTTransfer**/ 0248 0249 void BTTransfer::setPort(int port) 0250 { 0251 bt::Globals::instance().getTCPServer().changePort(port); 0252 if (BittorrentSettings::enableUTP()) 0253 bt::Globals::instance().getUTPServer().changePort(port + 1); 0254 } 0255 0256 void BTTransfer::setSpeedLimits(int ulLimit, int dlLimit) 0257 { 0258 qCDebug(KGET_DEBUG); 0259 if (!torrent) 0260 return; 0261 0262 torrent->setTrafficLimits(ulLimit * 1000, dlLimit * 1000); 0263 } 0264 0265 void BTTransfer::addTracker(const QString &url) 0266 { 0267 qCDebug(KGET_DEBUG); 0268 if (torrent->getStats().priv_torrent) { 0269 KMessageBox::error(nullptr, i18n("Cannot add a tracker to a private torrent.")); 0270 return; 0271 } 0272 0273 QUrl u(url); 0274 if (!u.isValid()) { 0275 KMessageBox::error(nullptr, i18n("Malformed URL.")); 0276 return; 0277 } 0278 0279 torrent->getTrackersList()->addTracker(u, true); 0280 } 0281 0282 /**Private functions**/ 0283 0284 void BTTransfer::startTorrent() 0285 { 0286 if (m_ready) { 0287 // qCDebug(KGET_DEBUG) << "Going to download that stuff :-0"; 0288 setSpeedLimits(uploadLimit(Transfer::InvisibleSpeedLimit), downloadLimit(Transfer::InvisibleSpeedLimit)); // Set traffic-limits before starting 0289 torrent->setMonitor(this); 0290 torrent->start(); 0291 timer.start(250); 0292 if (chunksTotal() == chunksDownloaded() /* && !m_downloadFinished*/) { 0293 slotDownloadFinished(torrent); 0294 } else { 0295 setStatus(Job::Running, i18nc("transfer state: downloading", "Downloading...."), "media-playback-start"); 0296 } 0297 m_totalSize = torrent->getStats().total_bytes_to_download; 0298 setTransferChange(Tc_Status | Tc_TrackersList | Tc_TotalSize, true); 0299 updateFilesStatus(); 0300 } 0301 } 0302 0303 void BTTransfer::stopTorrent() 0304 { 0305 torrent->stop(); 0306 torrent->setMonitor(nullptr); 0307 m_downloadSpeed = 0; 0308 timer.stop(); 0309 0310 if (m_downloadFinished) { 0311 setStatus(Job::Stopped, i18nc("transfer state: finished", "Finished"), "dialog-ok"); 0312 } else { 0313 setStatus(Job::Stopped, i18nc("transfer state: stopped", "Stopped"), "process-stop"); 0314 } 0315 setTransferChange(Tc_Status, true); 0316 0317 updateFilesStatus(); 0318 } 0319 0320 void BTTransfer::updateTorrent() 0321 { 0322 // qCDebug(KGET_DEBUG) << "Update torrent"; 0323 bt::UpdateCurrentTime(); 0324 bt::AuthenticationMonitor::instance().update(); 0325 torrent->update(); 0326 0327 ChangesFlags changesFlags = 0; 0328 0329 if (m_downloadedSize != (m_downloadedSize = torrent->getStats().bytes_downloaded)) 0330 changesFlags |= Tc_DownloadedSize; 0331 0332 if (m_uploadSpeed != static_cast<int>(torrent->getStats().upload_rate)) { 0333 m_uploadSpeed = torrent->getStats().upload_rate; 0334 changesFlags |= Tc_UploadSpeed; 0335 } 0336 0337 if (m_downloadSpeed != static_cast<int>(torrent->getStats().download_rate)) { 0338 m_downloadSpeed = torrent->getStats().download_rate; 0339 changesFlags |= Tc_DownloadSpeed; 0340 } 0341 0342 int percent = (chunksDownloaded() * 100) / chunksTotal(); 0343 if (m_percent != percent) { 0344 m_percent = percent; 0345 changesFlags |= Tc_Percent; 0346 } 0347 0348 setTransferChange(changesFlags, true); 0349 0350 // update the files status every 3 seconds 0351 if (!m_updateCounter) { 0352 updateFilesStatus(); 0353 m_updateCounter = 12; 0354 } 0355 --m_updateCounter; 0356 } 0357 0358 void BTTransfer::updateFilesStatus() 0359 { 0360 const Job::Status currentStatus = this->status(); 0361 if (!torrent) { 0362 return; 0363 } 0364 const bt::TorrentStats *stats = &torrent->getStats(); 0365 if (stats->multi_file_torrent) { 0366 QHash<QUrl, bt::TorrentFileInterface *>::const_iterator it; 0367 QHash<QUrl, bt::TorrentFileInterface *>::const_iterator itEnd = m_files.constEnd(); 0368 for (it = m_files.constBegin(); it != itEnd; ++it) { 0369 QModelIndex status = m_fileModel->index(it.key(), FileItem::Status); 0370 if (!(*it)->doNotDownload() && (currentStatus == Job::Running)) { 0371 m_fileModel->setData(status, Job::Running); 0372 } else { 0373 m_fileModel->setData(status, Job::Stopped); 0374 } 0375 if (qFuzzyCompare((*it)->getDownloadPercentage(), 100.0f)) { 0376 m_fileModel->setData(status, Job::Finished); 0377 } 0378 } 0379 } else { 0380 QModelIndexList indexes = fileModel()->fileIndexes(FileItem::Status); 0381 if (indexes.count() != 1) { 0382 return; 0383 } 0384 0385 QModelIndex index = indexes.first(); 0386 if (stats->bytes_left_to_download) { 0387 if (currentStatus == Job::Running) { 0388 fileModel()->setData(index, Job::Running); 0389 } else { 0390 fileModel()->setData(index, Job::Stopped); 0391 } 0392 } else { 0393 fileModel()->setData(index, Job::Finished); 0394 } 0395 } 0396 } 0397 0398 void BTTransfer::btTransferInit(const QUrl &src, const QByteArray &data) 0399 { 0400 Q_UNUSED(data) 0401 qCDebug(KGET_DEBUG); 0402 if (src != m_source && !src.isEmpty()) 0403 m_source = src; 0404 0405 QFile file(m_source.toLocalFile()); 0406 0407 if (!file.open(QIODevice::ReadOnly)) { 0408 setError(i18n("Torrent file does not exist"), "dialog-cancel", Job::NotSolveable, TorrentFileNotFoundError); 0409 setTransferChange(Tc_Status, true); 0410 resolveError(TorrentFileNotFoundError); 0411 return; 0412 } 0413 0414 setStatus(Job::Stopped, 0415 i18n("Analyzing torrent...."), 0416 "document-preview"); // jpetso says: you should probably use the "process-working" icon here (from the animations category), but that's a 0417 // multi-frame PNG so it's hard for me to test 0418 setTransferChange(Tc_Status, true); 0419 0420 bt::InitLog(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/torrentlog.log"), 0421 false, 0422 false); // initialize the torrent-log 0423 0424 bt::SetClientInfo("KGet", KGET_VERSION_MAJOR, KGET_VERSION_MINOR, KGET_VERSION_PATCH, bt::NORMAL, "KG"); // Set client info to KGet 0425 0426 bt::Uint16 i = 0; 0427 while (!bt::Globals::instance().initTCPServer(BittorrentSettings::port() + i) && i < 10) 0428 i++; 0429 0430 if (i == 10) { 0431 setError(i18n("Cannot initialize port..."), "dialog-cancel"); 0432 setTransferChange(Tc_Status); 0433 return; 0434 } 0435 if (BittorrentSettings::enableUTP()) { 0436 while (!bt::Globals::instance().initUTPServer(BittorrentSettings::port() + i) && i < 10) // We don't care if it fails for now as UTP is experimental... 0437 i++; 0438 } 0439 0440 QDir tmpDir(m_tmp + m_source.fileName().remove(".torrent")); 0441 if (tmpDir.exists()) { 0442 tmpDir.remove("torrent"); 0443 } 0444 try { 0445 torrent = new bt::TorrentControl(); 0446 0447 if (!BittorrentSettings::tmpDir().isEmpty() && QFileInfo(BittorrentSettings::tmpDir()).isDir()) { 0448 m_tmp = BittorrentSettings::tmpDir(); 0449 } 0450 0451 m_ready = true; 0452 0453 qDebug() << "Source:" << m_source.path() << "Destination:" << m_dest.path(); 0454 m_dest = m_dest.adjusted(QUrl::StripTrailingSlash); 0455 torrent->init(nullptr, 0456 file.readAll(), 0457 m_tmp + m_source.fileName().remove(".torrent"), 0458 QUrl::fromLocalFile(m_dest.adjusted(QUrl::RemoveFilename).path()).toLocalFile()); 0459 0460 m_dest = QUrl::fromLocalFile(torrent->getStats().output_path); 0461 if (!torrent->getStats().multi_file_torrent 0462 && (m_dest.fileName() != torrent->getStats().torrent_name)) // TODO check if this is needed, so if that case is true at some point 0463 { 0464 m_dest = m_dest.adjusted(QUrl::StripTrailingSlash); 0465 m_dest.setPath(m_dest.path() + '/' + (torrent->getStats().torrent_name)); 0466 } 0467 0468 torrent->createFiles(); 0469 0470 torrent->setPreallocateDiskSpace(BittorrentSettings::preAlloc()); 0471 0472 connect(torrent, SIGNAL(stoppedByError(bt::TorrentInterface *, QString)), SLOT(slotStoppedByError(bt::TorrentInterface *, QString))); 0473 connect(torrent, &bt::TorrentInterface::finished, this, &BTTransfer::slotDownloadFinished); 0474 // FIXME connect(tc,SIGNAL(corruptedDataFound(bt::TorrentInterface*)), this, SLOT(emitCorruptedData(bt::TorrentInterface*)));//TODO: Fix it 0475 } catch (bt::Error &err) { 0476 m_ready = false; 0477 torrent->deleteLater(); 0478 torrent = nullptr; 0479 setError(err.toString(), "dialog-cancel", Job::NotSolveable); 0480 setTransferChange(Tc_Status); 0481 return; 0482 } 0483 startTorrent(); 0484 connect(&timer, &QTimer::timeout, this, &BTTransfer::update); 0485 } 0486 0487 void BTTransfer::slotStoppedByError(const bt::TorrentInterface *&error, const QString &errormsg) 0488 { 0489 Q_UNUSED(error) 0490 stop(); 0491 setError(errormsg, "dialog-cancel", Job::NotSolveable); 0492 setTransferChange(Tc_Status); 0493 } 0494 0495 void BTTransfer::slotDownloadFinished(bt::TorrentInterface *ti) 0496 { 0497 qCDebug(KGET_DEBUG) << "Start seeding *********************************************************************"; 0498 Q_UNUSED(ti) 0499 m_downloadFinished = true; 0500 // timer.stop(); 0501 setStatus(Job::FinishedKeepAlive, i18nc("Transfer status: seeding", "Seeding...."), "media-playback-start"); 0502 setTransferChange(Tc_Status, true); 0503 } 0504 0505 /**Property-Functions**/ 0506 QList<QUrl> BTTransfer::trackersList() const 0507 { 0508 if (!torrent) 0509 return QList<QUrl>(); 0510 0511 QList<QUrl> trackers; 0512 foreach (bt::TrackerInterface *tracker, torrent->getTrackersList()->getTrackers()) 0513 trackers << tracker->trackerURL(); 0514 return trackers; 0515 } 0516 0517 int BTTransfer::sessionBytesDownloaded() const 0518 { 0519 if (!torrent) 0520 return -1; 0521 0522 return torrent->getStats().session_bytes_downloaded; 0523 } 0524 0525 int BTTransfer::sessionBytesUploaded() const 0526 { 0527 if (!torrent) 0528 return -1; 0529 0530 return torrent->getStats().session_bytes_uploaded; 0531 } 0532 0533 int BTTransfer::chunksTotal() const 0534 { 0535 if (!torrent) 0536 return -1; 0537 0538 return torrent->getTorrent().getNumChunks(); 0539 } 0540 0541 int BTTransfer::chunksDownloaded() const 0542 { 0543 if (!torrent) 0544 return -1; 0545 0546 return torrent->downloadedChunksBitSet().numOnBits(); 0547 } 0548 0549 int BTTransfer::chunksExcluded() const 0550 { 0551 if (!torrent) 0552 return -1; 0553 0554 return torrent->excludedChunksBitSet().numOnBits(); 0555 } 0556 0557 int BTTransfer::chunksLeft() const 0558 { 0559 if (!torrent) 0560 return -1; 0561 0562 return chunksTotal() - chunksDownloaded(); 0563 } 0564 0565 int BTTransfer::seedsConnected() const 0566 { 0567 if (!torrent) 0568 return -1; 0569 0570 return torrent->getStats().seeders_connected_to; 0571 } 0572 0573 int BTTransfer::seedsDisconnected() const 0574 { 0575 if (!torrent) 0576 return -1; 0577 0578 return torrent->getStats().seeders_total; 0579 } 0580 0581 int BTTransfer::leechesConnected() const 0582 { 0583 if (!torrent) 0584 return -1; 0585 0586 return torrent->getStats().leechers_connected_to; 0587 } 0588 0589 int BTTransfer::leechesDisconnected() const 0590 { 0591 if (!torrent) 0592 return -1; 0593 0594 return torrent->getStats().leechers_total; 0595 } 0596 0597 int BTTransfer::elapsedTime() const 0598 { 0599 if (!torrent) 0600 return -1; 0601 0602 return torrent->getRunningTimeDL(); 0603 } 0604 0605 int BTTransfer::remainingTime() const 0606 { 0607 if (!torrent) 0608 return Transfer::remainingTime(); 0609 0610 return torrent->getETA(); 0611 } 0612 0613 bt::TorrentControl *BTTransfer::torrentControl() 0614 { 0615 return torrent; 0616 } 0617 0618 bool BTTransfer::ready() 0619 { 0620 return m_ready; 0621 } 0622 0623 void BTTransfer::downloadRemoved(bt::ChunkDownloadInterface *cd) 0624 { 0625 if (static_cast<BTTransferHandler *>(handler())->torrentMonitor()) 0626 static_cast<BTTransferHandler *>(handler())->torrentMonitor()->downloadRemoved(cd); 0627 0628 setTransferChange(Tc_ChunksDownloaded | Tc_ChunksExcluded | Tc_ChunksLeft, true); 0629 } 0630 0631 void BTTransfer::downloadStarted(bt::ChunkDownloadInterface *cd) 0632 { 0633 if (static_cast<BTTransferHandler *>(handler())->torrentMonitor()) 0634 static_cast<BTTransferHandler *>(handler())->torrentMonitor()->downloadStarted(cd); 0635 0636 setTransferChange(Tc_ChunksDownloaded | Tc_ChunksExcluded | Tc_ChunksLeft, true); 0637 } 0638 0639 void BTTransfer::peerAdded(bt::PeerInterface *peer) 0640 { 0641 if (static_cast<BTTransferHandler *>(handler())->torrentMonitor()) 0642 static_cast<BTTransferHandler *>(handler())->torrentMonitor()->peerAdded(peer); 0643 0644 setTransferChange(Tc_SeedsConnected | Tc_SeedsDisconnected | Tc_LeechesConnected | Tc_LeechesDisconnected, true); 0645 } 0646 0647 void BTTransfer::peerRemoved(bt::PeerInterface *peer) 0648 { 0649 if (static_cast<BTTransferHandler *>(handler())->torrentMonitor()) 0650 static_cast<BTTransferHandler *>(handler())->torrentMonitor()->peerRemoved(peer); 0651 0652 setTransferChange(Tc_SeedsConnected | Tc_SeedsDisconnected | Tc_LeechesConnected | Tc_LeechesDisconnected, true); 0653 } 0654 0655 void BTTransfer::stopped() 0656 { 0657 if (static_cast<BTTransferHandler *>(handler())->torrentMonitor()) 0658 static_cast<BTTransferHandler *>(handler())->torrentMonitor()->stopped(); 0659 } 0660 0661 void BTTransfer::destroyed() 0662 { 0663 if (static_cast<BTTransferHandler *>(handler())->torrentMonitor()) 0664 static_cast<BTTransferHandler *>(handler())->torrentMonitor()->destroyed(); 0665 } 0666 0667 QList<QUrl> BTTransfer::files() const 0668 { 0669 QList<QUrl> urls; 0670 0671 if (!torrent) { 0672 return urls; 0673 } 0674 0675 // multiple files 0676 if (torrent->getStats().multi_file_torrent) { 0677 for (uint i = 0; i < torrent->getNumFiles(); ++i) { 0678 const QString path = torrent->getTorrentFile(i).getPathOnDisk(); 0679 urls.append(QUrl(path)); 0680 } 0681 } 0682 // one single file 0683 else { 0684 QUrl temp = m_dest; 0685 if (m_dest.fileName() != torrent->getStats().torrent_name) // TODO check if the body is ever entered! 0686 { 0687 temp = temp.adjusted(QUrl::StripTrailingSlash); 0688 temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name)); 0689 } 0690 urls.append(temp); 0691 } 0692 0693 return urls; 0694 } 0695 0696 void BTTransfer::filesSelected() 0697 { 0698 QModelIndexList indexes = fileModel()->fileIndexes(FileItem::File); 0699 // one single file 0700 if (indexes.count() == 1) { 0701 QModelIndex index = indexes.first(); 0702 const bool doDownload = index.data(Qt::CheckStateRole).toBool(); 0703 if (torrent && torrent->getStats().bytes_left_to_download) { 0704 if (doDownload) { 0705 start(); 0706 } else { 0707 stop(); 0708 } 0709 } 0710 } 0711 // multiple files 0712 else { 0713 foreach (const QModelIndex &index, indexes) { 0714 const QUrl dest = fileModel()->getUrl(index); 0715 const bool doDownload = index.data(Qt::CheckStateRole).toBool(); 0716 bt::TorrentFileInterface *file = m_files[dest]; 0717 file->setDoNotDownload(!doDownload); 0718 } 0719 } 0720 0721 // setTransferChange(Tc_TotalSize | Tc_DownloadedSize | Tc_Percent, true); 0722 } 0723 0724 FileModel *BTTransfer::fileModel() // TODO correct file model for one-file-torrents 0725 { 0726 if (!m_fileModel) { 0727 if (!torrent) { 0728 return nullptr; 0729 } 0730 0731 // multiple files 0732 if (torrent->getStats().multi_file_torrent) { 0733 for (bt::Uint32 i = 0; i < torrent->getNumFiles(); ++i) { 0734 bt::TorrentFileInterface *file = &torrent->getTorrentFile(i); 0735 m_files[QUrl(file->getPathOnDisk())] = file; 0736 } 0737 m_fileModel = new FileModel(m_files.keys(), directory(), this); 0738 // connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl))); 0739 connect(m_fileModel, &FileModel::checkStateChanged, this, &BTTransfer::filesSelected); 0740 0741 // set the checkstate, the status and the size of the model items 0742 QHash<QUrl, bt::TorrentFileInterface *>::const_iterator it; 0743 QHash<QUrl, bt::TorrentFileInterface *>::const_iterator itEnd = m_files.constEnd(); 0744 const Job::Status curentStatus = this->status(); 0745 for (it = m_files.constBegin(); it != itEnd; ++it) { 0746 QModelIndex size = m_fileModel->index(it.key(), FileItem::Size); 0747 m_fileModel->setData(size, static_cast<qlonglong>((*it)->getSize())); 0748 0749 const bool doDownload = !(*it)->doNotDownload(); 0750 QModelIndex checkIndex = m_fileModel->index(it.key(), FileItem::File); 0751 const Qt::CheckState checkState = doDownload ? Qt::Checked : Qt::Unchecked; 0752 m_fileModel->setData(checkIndex, checkState, Qt::CheckStateRole); 0753 0754 QModelIndex status = m_fileModel->index(it.key(), FileItem::Status); 0755 if (doDownload && (curentStatus == Job::Running)) { 0756 m_fileModel->setData(status, Job::Running); 0757 } else { 0758 m_fileModel->setData(status, Job::Stopped); 0759 } 0760 if (qFuzzyCompare((*it)->getDownloadPercentage(), 100.0f)) { 0761 m_fileModel->setData(status, Job::Finished); 0762 } 0763 } 0764 } 0765 // one single file 0766 else { 0767 QList<QUrl> urls; 0768 QUrl temp = m_dest; 0769 if (m_dest.fileName() != torrent->getStats().torrent_name) // TODO check if the body is ever entered! 0770 { 0771 temp = temp.adjusted(QUrl::StripTrailingSlash); 0772 temp.setPath(temp.path() + '/' + (torrent->getStats().torrent_name)); 0773 } 0774 const QUrl url = temp; 0775 urls.append(url); 0776 0777 m_fileModel = new FileModel(urls, directory(), this); 0778 // connect(m_fileModel, SIGNAL(rename(QUrl,QUrl)), this, SLOT(slotRename(QUrl,QUrl))); 0779 connect(m_fileModel, &FileModel::checkStateChanged, this, &BTTransfer::filesSelected); 0780 0781 QModelIndex size = m_fileModel->index(url, FileItem::Size); 0782 m_fileModel->setData(size, static_cast<qlonglong>(torrent->getStats().total_bytes)); 0783 0784 QModelIndex checkIndex = m_fileModel->index(url, FileItem::File); 0785 m_fileModel->setData(checkIndex, Qt::Checked, Qt::CheckStateRole); 0786 0787 QModelIndex status = m_fileModel->index(url, FileItem::Status); 0788 if (this->status() == Job::Running) { 0789 m_fileModel->setData(status, Job::Running); 0790 } else { 0791 m_fileModel->setData(status, Job::Stopped); 0792 } 0793 if (!torrent->getStats().bytes_left_to_download) { 0794 m_fileModel->setData(status, Job::Finished); 0795 } 0796 } 0797 } 0798 0799 return m_fileModel; 0800 } 0801 0802 #include "moc_bttransfer.cpp"