From a03ad3de23e0b8dd9d47f9c9ba1aaa2a027e1731 Mon Sep 17 00:00:00 2001 From: Christophe Dumez Date: Sun, 31 Jan 2010 13:09:46 +0000 Subject: [PATCH] FEATURE: Torrent files can be exported to a given directory BUGFIX: Fix crash when double-clicking on a torrent that has no metadata to open its save path --- Changelog | 1 + src/bittorrent.cpp | 103 ++++++++++++++++++++++++++++++++----- src/bittorrent.h | 2 + src/misc.h | 21 ++++++++ src/options_imp.cpp | 36 +++++++++++-- src/options_imp.h | 2 + src/preferences.h | 18 +++++++ src/transferlistwidget.cpp | 5 +- src/ui/options.ui | 63 +++++++++++++++++++---- 9 files changed, 225 insertions(+), 26 deletions(-) diff --git a/Changelog b/Changelog index d0e9366ac..9094019d4 100644 --- a/Changelog +++ b/Changelog @@ -3,6 +3,7 @@ - FEATURE: Bandwidth scheduler (automatically use alternative speed limits for a given period) - FEATURE: Added "Added/Completed On" columns to transfer list - FEATURE: Added "Upload/Download limit" columns to transfer list + - FEATURE: Torrent files can be exported to a given directory * Mon Jan 18 2010 - Christophe Dumez - v2.1.0 - FEATURE: Graphical User Interface can be disabled at compilation time (headless running) diff --git a/src/bittorrent.cpp b/src/bittorrent.cpp index 7a9645032..574109dd7 100644 --- a/src/bittorrent.cpp +++ b/src/bittorrent.cpp @@ -68,7 +68,7 @@ enum ProxyType {HTTP=1, SOCKS5=2, HTTP_PW=3, SOCKS5_PW=4, SOCKS4=5}; enum VersionType { NORMAL,ALPHA,BETA,RELEASE_CANDIDATE,DEVEL }; // Main constructor -Bittorrent::Bittorrent() : preAllocateAll(false), addInPause(false), ratio_limit(-1), UPnPEnabled(false), NATPMPEnabled(false), LSDEnabled(false), DHTEnabled(false), current_dht_port(0), queueingEnabled(false), exiting(false) { +Bittorrent::Bittorrent() : preAllocateAll(false), addInPause(false), ratio_limit(-1), UPnPEnabled(false), NATPMPEnabled(false), LSDEnabled(false), DHTEnabled(false), current_dht_port(0), queueingEnabled(false), torrentExport(false), exiting(false) { #ifndef DISABLE_GUI geoipDBLoaded = false; resolve_countries = false; @@ -280,6 +280,15 @@ void Bittorrent::configureSession() { //Interval first enableDirectoryScanning(scan_dir); } + // * Export Dir + bool newTorrentExport = Preferences::isTorrentExportEnabled(); + if(torrentExport != newTorrentExport) { + torrentExport = newTorrentExport; + if(torrentExport) { + qDebug("Torrent export is enabled, exporting the current torrents"); + exportTorrentFiles(Preferences::getExportDir()); + } + } // Connection // * Ports binding unsigned short old_listenPort = getListenPort(); @@ -1086,14 +1095,28 @@ QTorrentHandle Bittorrent::addTorrent(QString path, bool fromScanDir, QString fr if(appendqBExtension) appendqBextensionToTorrent(h, true); #endif - } - QString newFile = torrentBackup.path() + QDir::separator() + hash + ".torrent"; - if(file != newFile) { - // Delete file from torrentBackup directory in case it exists because - // QFile::copy() do not overwrite - QFile::remove(newFile); - // Copy it to torrentBackup directory - QFile::copy(file, newFile); + // Backup torrent file + QString newFile = torrentBackup.absoluteFilePath(hash + ".torrent"); + if(file != newFile) { + // Delete file from torrentBackup directory in case it exists because + // QFile::copy() do not overwrite + QFile::remove(newFile); + // Copy it to torrentBackup directory + QFile::copy(file, newFile); + } + // Copy the torrent file to the export folder + if(torrentExport) { + QDir exportPath(Preferences::getExportDir()); + if(exportPath.exists() || exportPath.mkpath(exportPath.absolutePath())) { + QString torrent_path = exportPath.absoluteFilePath(h.name()+".torrent"); + if(QFile::exists(torrent_path) && misc::sameFiles(file, torrent_path)) { + // Append hash to torrent name to make it unique + torrent_path = exportPath.absoluteFilePath(h.name()+"-"+h.hash()+".torrent"); + } + QFile::copy(file, torrent_path); + //h.save_torrent_file(torrent_path); + } + } } if(!fastResume && (!addInPause || (Preferences::useAdditionDialog() && !fromScanDir))) { // Start torrent because it was added in paused state @@ -1121,6 +1144,43 @@ QTorrentHandle Bittorrent::addTorrent(QString path, bool fromScanDir, QString fr return h; } +void Bittorrent::exportTorrentFiles(QString path) { + Q_ASSERT(torrentExport); + QDir exportDir(path); + if(!exportDir.exists()) { + if(!exportDir.mkpath(exportDir.absolutePath())) { + std::cerr << "Error: Could not create torrent export directory: " << exportDir.absolutePath().toLocal8Bit().data() << std::endl; + return; + } + } + QDir torrentBackup(misc::BTBackupLocation()); + std::vector handles = s->get_torrents(); + std::vector::iterator itr; + for(itr=handles.begin(); itr != handles.end(); itr++) { + QTorrentHandle h(*itr); + if(!h.is_valid()) { + std::cerr << "Torrent Export: torrent is invalid, skipping..." << std::endl; + continue; + } + QString src_path = torrentBackup.absoluteFilePath(h.hash()+".torrent"); + if(QFile::exists(src_path)) { + QString dst_path = exportDir.absoluteFilePath(h.name()+".torrent"); + if(QFile::exists(dst_path)) { + if(!misc::sameFiles(src_path, dst_path)) { + dst_path = exportDir.absoluteFilePath(h.name()+"-"+h.hash()+".torrent"); + } else { + qDebug("Torrent Export: Destination file exists, skipping..."); + continue; + } + } + qDebug("Export Torrent: %s -> %s", src_path.toLocal8Bit().data(), dst_path.toLocal8Bit().data()); + QFile::copy(src_path, dst_path); + } else { + std::cerr << "Error: could not export torrent "<< h.hash().toLocal8Bit().data() << ", maybe it has not metadata yet." <set_max_connections(maxConnec); @@ -1746,7 +1806,7 @@ void Bittorrent::addConsoleMessage(QString msg, QString) { // Remember finished state TorrentPersistentData::saveSeedStatus(h); #ifdef LIBTORRENT_0_15 - // Remove .!qB extension if necessary + // Remove .!qB extension if necessary if(appendqBExtension) appendqBextensionToTorrent(h, false); #endif @@ -1791,8 +1851,7 @@ void Bittorrent::addConsoleMessage(QString msg, QString) { if(QFile::exists(file)) QFile::remove(file); qDebug("Saving fastresume data in %s", file.toLocal8Bit().data()); - if (p->resume_data) - { + if (p->resume_data) { boost::filesystem::ofstream out(fs::path(torrentBackup.path().toLocal8Bit().data()) / file.toLocal8Bit().data(), std::ios_base::binary); out.unsetf(std::ios_base::skipws); bencode(std::ostream_iterator(out), *p->resume_data); @@ -1818,6 +1877,26 @@ void Bittorrent::addConsoleMessage(QString msg, QString) { QDir torrentBackup(misc::BTBackupLocation()); if(!QFile::exists(torrentBackup.path()+QDir::separator()+h.hash()+QString(".torrent"))) h.save_torrent_file(torrentBackup.path()+QDir::separator()+h.hash()+QString(".torrent")); + // Copy the torrent file to the export folder + if(torrentExport) { + QDir exportPath(Preferences::getExportDir()); + if(exportPath.exists() || exportPath.mkpath(exportPath.absolutePath())) { + QString torrent_path = exportPath.absoluteFilePath(h.name()+".torrent"); + bool duplicate = false; + if(QFile::exists(torrent_path)) { + // Append hash to torrent name to make it unique + torrent_path = exportPath.absoluteFilePath(h.name()+"-"+h.hash()+".torrent"); + duplicate = true; + } + h.save_torrent_file(torrent_path); + if(duplicate) { + // Remove duplicate file if indentical + if(misc::sameFiles(exportPath.absoluteFilePath(h.name()+".torrent"), torrent_path)) { + QFile::remove(torrent_path); + } + } + } + } if(h.is_paused()) { // XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert // and the torrent can be paused when metadata is received diff --git a/src/bittorrent.h b/src/bittorrent.h index 8a75c96d7..822d03209 100644 --- a/src/bittorrent.h +++ b/src/bittorrent.h @@ -118,6 +118,7 @@ private: bool PeXEnabled; bool queueingEnabled; bool appendLabelToSavePath; + bool torrentExport; #ifdef LIBTORRENT_0_15 bool appendqBExtension; #endif @@ -247,6 +248,7 @@ protected slots: void readAlerts(); void deleteBigRatios(); void takeETASamples(); + void exportTorrentFiles(QString path); signals: void addedTorrent(QTorrentHandle& h); diff --git a/src/misc.h b/src/misc.h index 56264dbf5..59eb58b53 100644 --- a/src/misc.h +++ b/src/misc.h @@ -124,6 +124,27 @@ public: return x; } + static bool sameFiles(QString path1, QString path2) { + QFile f1(path1); + if(!f1.exists()) return false; + QFile f2(path2); + if(!f2.exists()) return false; + QByteArray content1, content2; + if(f1.open(QIODevice::ReadOnly)) { + content1 = f1.readAll(); + f1.close(); + } else { + return false; + } + if(f2.open(QIODevice::ReadOnly)) { + content1 = f2.readAll(); + f2.close(); + } else { + return false; + } + return content1 == content2; + } + static void copyDir(QString src_path, QString dst_path) { QDir sourceDir(src_path); if(!sourceDir.exists()) return; diff --git a/src/options_imp.cpp b/src/options_imp.cpp index 77b5e31e4..b0ed8bd40 100644 --- a/src/options_imp.cpp +++ b/src/options_imp.cpp @@ -153,9 +153,7 @@ options_imp::options_imp(QWidget *parent):QDialog(parent){ // Downloads tab connect(checkTempFolder, SIGNAL(toggled(bool)), this, SLOT(enableTempPathInput(bool))); connect(checkScanDir, SIGNAL(toggled(bool)), this, SLOT(enableDirScan(bool))); - connect(actionTorrentDlOnDblClBox, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton())); - connect(actionTorrentFnOnDblClBox, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton())); - connect(checkTempFolder, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(checkExportDir, SIGNAL(toggled(bool)), this, SLOT(enableTorrentExport(bool))); // Connection tab connect(checkUploadLimit, SIGNAL(toggled(bool)), this, SLOT(enableUploadLimit(bool))); connect(checkDownloadLimit, SIGNAL(toggled(bool)), this, SLOT(enableDownloadLimit(bool))); @@ -206,6 +204,11 @@ options_imp::options_imp(QWidget *parent):QDialog(parent){ connect(checkStartPaused, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(checkScanDir, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(textScanDir, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); + connect(checkExportDir, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); + connect(textExportDir, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); + connect(actionTorrentDlOnDblClBox, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton())); + connect(actionTorrentFnOnDblClBox, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton())); + connect(checkTempFolder, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); // Connection tab connect(spinPort, SIGNAL(valueChanged(QString)), this, SLOT(enableApplyButton())); connect(checkUPnP, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); @@ -394,6 +397,7 @@ void options_imp::saveOptions(){ settings.setValue(QString::fromUtf8("AdditionDialog"), useAdditionDialog()); settings.setValue(QString::fromUtf8("StartInPause"), addTorrentsInPause()); settings.setValue(QString::fromUtf8("ScanDir"), getScanDir()); + Preferences::setExportDir(getExportDir()); settings.setValue(QString::fromUtf8("DblClOnTorDl"), getActionOnDblClOnTorrentDl()); settings.setValue(QString::fromUtf8("DblClOnTorFn"), getActionOnDblClOnTorrentFn()); // End Downloads preferences @@ -627,6 +631,19 @@ void options_imp::loadOptions(){ textScanDir->setText(strValue); enableDirScan(checkScanDir->isChecked()); } + + strValue = Preferences::getExportDir(); + if(strValue.isEmpty()) { + // Disable + checkExportDir->setChecked(false); + enableTorrentExport(checkExportDir->isChecked()); + } else { + // enable + checkExportDir->setChecked(true); + textExportDir->setText(strValue); + enableTorrentExport(checkExportDir->isChecked()); + } + intValue = Preferences::getActionOnDblClOnTorrentDl(); if(intValue >= actionTorrentDlOnDblClBox->count()) intValue = 0; @@ -1267,6 +1284,11 @@ void options_imp::enableDirScan(bool checked){ browseScanDirButton->setEnabled(checked); } +void options_imp::enableTorrentExport(bool checked) { + textExportDir->setEnabled(checked); + browseExportDirButton->setEnabled(checked); +} + bool options_imp::isSlashScreenDisabled() const { return checkNoSplash->isChecked(); } @@ -1365,6 +1387,14 @@ QString options_imp::getScanDir() const { } } +QString options_imp::getExportDir() const { + if(checkExportDir->isChecked()){ + return misc::expandPath(textExportDir->text()); + }else{ + return QString::null; + } +} + // Return action on double-click on a downloading torrent set in options int options_imp::getActionOnDblClOnTorrentDl() const { if(actionTorrentDlOnDblClBox->currentIndex()<1) diff --git a/src/options_imp.h b/src/options_imp.h index 57933bf49..7544acf8a 100644 --- a/src/options_imp.h +++ b/src/options_imp.h @@ -83,6 +83,7 @@ protected: bool addTorrentsInPause() const; bool isDirScanEnabled() const; QString getScanDir() const; + QString getExportDir() const; int getActionOnDblClOnTorrentDl() const; int getActionOnDblClOnTorrentFn() const; // Connection options @@ -135,6 +136,7 @@ protected slots: void enableDownloadLimit(bool checked); void enableTempPathInput(bool checked); void enableDirScan(bool checked); + void enableTorrentExport(bool checked); void enablePeerProxy(int comboIndex); void enablePeerProxyAuth(bool checked); void enableHTTPProxy(int comboIndex); diff --git a/src/preferences.h b/src/preferences.h index 2ab615a55..c65229f7d 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -214,6 +214,24 @@ public: settings.setValue(QString::fromUtf8("Preferences/Downloads/ScanDir"), path); } + static bool isTorrentExportEnabled() { + QSettings settings("qBittorrent", "qBittorrent"); + return !settings.value(QString::fromUtf8("Preferences/Downloads/TorrentExport"), QString()).toString().isEmpty(); + } + + static QString getExportDir() { + QSettings settings("qBittorrent", "qBittorrent"); + return settings.value(QString::fromUtf8("Preferences/Downloads/TorrentExport"), QString()).toString(); + } + + static void setExportDir(QString path) { + path = path.trimmed(); + if(path.isEmpty()) + path = QString(); + QSettings settings("qBittorrent", "qBittorrent"); + settings.setValue(QString::fromUtf8("Preferences/Downloads/TorrentExport"), path); + } + static int getActionOnDblClOnTorrentDl() { QSettings settings("qBittorrent", "qBittorrent"); return settings.value(QString::fromUtf8("Preferences/Downloads/DblClOnTorDl"), 0).toInt(); diff --git a/src/transferlistwidget.cpp b/src/transferlistwidget.cpp index f83f21205..40181c34e 100644 --- a/src/transferlistwidget.cpp +++ b/src/transferlistwidget.cpp @@ -581,7 +581,10 @@ void TransferListWidget::torrentDoubleClicked(QModelIndex index) { } break; case OPEN_DEST: - QDesktopServices::openUrl("file://" + h.root_path()); + if(h.has_metadata()) + QDesktopServices::openUrl("file://" + h.root_path()); + else + QDesktopServices::openUrl("file://" + h.save_path()); break; } } diff --git a/src/ui/options.ui b/src/ui/options.ui index 50df63baf..0f75e61fa 100644 --- a/src/ui/options.ui +++ b/src/ui/options.ui @@ -629,7 +629,7 @@ QGroupBox { 0 0 644 - 504 + 583 @@ -645,9 +645,6 @@ QGroupBox { File system - - 3 - @@ -811,6 +808,52 @@ QGroupBox { + + + + Copy .torrent files to: + + + + + + + + + false + + + QLineEdit { + margin-left: 23px; +} + + + + + + + false + + + + 22 + 22 + + + + + 25 + 27 + + + + + :/Icons/oxygen/browse.png:/Icons/oxygen/browse.png + + + + + @@ -1393,8 +1436,8 @@ QGroupBox { 0 0 - 620 - 490 + 366 + 332 @@ -1798,8 +1841,8 @@ QGroupBox { 0 0 - 620 - 490 + 466 + 415 @@ -2746,8 +2789,8 @@ QGroupBox { 0 0 - 219 - 221 + 620 + 490