Browse Source

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
adaptive-webui-19844
Christophe Dumez 15 years ago
parent
commit
a03ad3de23
  1. 1
      Changelog
  2. 103
      src/bittorrent.cpp
  3. 2
      src/bittorrent.h
  4. 21
      src/misc.h
  5. 36
      src/options_imp.cpp
  6. 2
      src/options_imp.h
  7. 18
      src/preferences.h
  8. 5
      src/transferlistwidget.cpp
  9. 63
      src/ui/options.ui

1
Changelog

@ -3,6 +3,7 @@
- FEATURE: Bandwidth scheduler (automatically use alternative speed limits for a given period) - FEATURE: Bandwidth scheduler (automatically use alternative speed limits for a given period)
- FEATURE: Added "Added/Completed On" columns to transfer list - FEATURE: Added "Added/Completed On" columns to transfer list
- FEATURE: Added "Upload/Download limit" 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 <chris@qbittorrent.org> - v2.1.0 * Mon Jan 18 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.1.0
- FEATURE: Graphical User Interface can be disabled at compilation time (headless running) - FEATURE: Graphical User Interface can be disabled at compilation time (headless running)

103
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 }; enum VersionType { NORMAL,ALPHA,BETA,RELEASE_CANDIDATE,DEVEL };
// Main constructor // 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 #ifndef DISABLE_GUI
geoipDBLoaded = false; geoipDBLoaded = false;
resolve_countries = false; resolve_countries = false;
@ -280,6 +280,15 @@ void Bittorrent::configureSession() {
//Interval first //Interval first
enableDirectoryScanning(scan_dir); 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 // Connection
// * Ports binding // * Ports binding
unsigned short old_listenPort = getListenPort(); unsigned short old_listenPort = getListenPort();
@ -1086,14 +1095,28 @@ QTorrentHandle Bittorrent::addTorrent(QString path, bool fromScanDir, QString fr
if(appendqBExtension) if(appendqBExtension)
appendqBextensionToTorrent(h, true); appendqBextensionToTorrent(h, true);
#endif #endif
} // Backup torrent file
QString newFile = torrentBackup.path() + QDir::separator() + hash + ".torrent"; QString newFile = torrentBackup.absoluteFilePath(hash + ".torrent");
if(file != newFile) { if(file != newFile) {
// Delete file from torrentBackup directory in case it exists because // Delete file from torrentBackup directory in case it exists because
// QFile::copy() do not overwrite // QFile::copy() do not overwrite
QFile::remove(newFile); QFile::remove(newFile);
// Copy it to torrentBackup directory // Copy it to torrentBackup directory
QFile::copy(file, newFile); 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))) { if(!fastResume && (!addInPause || (Preferences::useAdditionDialog() && !fromScanDir))) {
// Start torrent because it was added in paused state // Start torrent because it was added in paused state
@ -1121,6 +1144,43 @@ QTorrentHandle Bittorrent::addTorrent(QString path, bool fromScanDir, QString fr
return h; 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<torrent_handle> handles = s->get_torrents();
std::vector<torrent_handle>::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." <<std::endl;
}
}
}
// Set the maximum number of opened connections // Set the maximum number of opened connections
void Bittorrent::setMaxConnections(int maxConnec) { void Bittorrent::setMaxConnections(int maxConnec) {
s->set_max_connections(maxConnec); s->set_max_connections(maxConnec);
@ -1746,7 +1806,7 @@ void Bittorrent::addConsoleMessage(QString msg, QString) {
// Remember finished state // Remember finished state
TorrentPersistentData::saveSeedStatus(h); TorrentPersistentData::saveSeedStatus(h);
#ifdef LIBTORRENT_0_15 #ifdef LIBTORRENT_0_15
// Remove .!qB extension if necessary // Remove .!qB extension if necessary
if(appendqBExtension) if(appendqBExtension)
appendqBextensionToTorrent(h, false); appendqBextensionToTorrent(h, false);
#endif #endif
@ -1791,8 +1851,7 @@ void Bittorrent::addConsoleMessage(QString msg, QString) {
if(QFile::exists(file)) if(QFile::exists(file))
QFile::remove(file); QFile::remove(file);
qDebug("Saving fastresume data in %s", file.toLocal8Bit().data()); 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); boost::filesystem::ofstream out(fs::path(torrentBackup.path().toLocal8Bit().data()) / file.toLocal8Bit().data(), std::ios_base::binary);
out.unsetf(std::ios_base::skipws); out.unsetf(std::ios_base::skipws);
bencode(std::ostream_iterator<char>(out), *p->resume_data); bencode(std::ostream_iterator<char>(out), *p->resume_data);
@ -1818,6 +1877,26 @@ void Bittorrent::addConsoleMessage(QString msg, QString) {
QDir torrentBackup(misc::BTBackupLocation()); QDir torrentBackup(misc::BTBackupLocation());
if(!QFile::exists(torrentBackup.path()+QDir::separator()+h.hash()+QString(".torrent"))) if(!QFile::exists(torrentBackup.path()+QDir::separator()+h.hash()+QString(".torrent")))
h.save_torrent_file(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()) { if(h.is_paused()) {
// XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert // XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert
// and the torrent can be paused when metadata is received // and the torrent can be paused when metadata is received

2
src/bittorrent.h

@ -118,6 +118,7 @@ private:
bool PeXEnabled; bool PeXEnabled;
bool queueingEnabled; bool queueingEnabled;
bool appendLabelToSavePath; bool appendLabelToSavePath;
bool torrentExport;
#ifdef LIBTORRENT_0_15 #ifdef LIBTORRENT_0_15
bool appendqBExtension; bool appendqBExtension;
#endif #endif
@ -247,6 +248,7 @@ protected slots:
void readAlerts(); void readAlerts();
void deleteBigRatios(); void deleteBigRatios();
void takeETASamples(); void takeETASamples();
void exportTorrentFiles(QString path);
signals: signals:
void addedTorrent(QTorrentHandle& h); void addedTorrent(QTorrentHandle& h);

21
src/misc.h

@ -124,6 +124,27 @@ public:
return x; 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) { static void copyDir(QString src_path, QString dst_path) {
QDir sourceDir(src_path); QDir sourceDir(src_path);
if(!sourceDir.exists()) return; if(!sourceDir.exists()) return;

36
src/options_imp.cpp

@ -153,9 +153,7 @@ options_imp::options_imp(QWidget *parent):QDialog(parent){
// Downloads tab // Downloads tab
connect(checkTempFolder, SIGNAL(toggled(bool)), this, SLOT(enableTempPathInput(bool))); connect(checkTempFolder, SIGNAL(toggled(bool)), this, SLOT(enableTempPathInput(bool)));
connect(checkScanDir, SIGNAL(toggled(bool)), this, SLOT(enableDirScan(bool))); connect(checkScanDir, SIGNAL(toggled(bool)), this, SLOT(enableDirScan(bool)));
connect(actionTorrentDlOnDblClBox, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton())); connect(checkExportDir, SIGNAL(toggled(bool)), this, SLOT(enableTorrentExport(bool)));
connect(actionTorrentFnOnDblClBox, SIGNAL(currentIndexChanged(int)), this, SLOT(enableApplyButton()));
connect(checkTempFolder, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
// Connection tab // Connection tab
connect(checkUploadLimit, SIGNAL(toggled(bool)), this, SLOT(enableUploadLimit(bool))); connect(checkUploadLimit, SIGNAL(toggled(bool)), this, SLOT(enableUploadLimit(bool)));
connect(checkDownloadLimit, SIGNAL(toggled(bool)), this, SLOT(enableDownloadLimit(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(checkStartPaused, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(checkScanDir, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(checkScanDir, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
connect(textScanDir, SIGNAL(textChanged(QString)), 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 // Connection tab
connect(spinPort, SIGNAL(valueChanged(QString)), this, SLOT(enableApplyButton())); connect(spinPort, SIGNAL(valueChanged(QString)), this, SLOT(enableApplyButton()));
connect(checkUPnP, SIGNAL(toggled(bool)), 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("AdditionDialog"), useAdditionDialog());
settings.setValue(QString::fromUtf8("StartInPause"), addTorrentsInPause()); settings.setValue(QString::fromUtf8("StartInPause"), addTorrentsInPause());
settings.setValue(QString::fromUtf8("ScanDir"), getScanDir()); settings.setValue(QString::fromUtf8("ScanDir"), getScanDir());
Preferences::setExportDir(getExportDir());
settings.setValue(QString::fromUtf8("DblClOnTorDl"), getActionOnDblClOnTorrentDl()); settings.setValue(QString::fromUtf8("DblClOnTorDl"), getActionOnDblClOnTorrentDl());
settings.setValue(QString::fromUtf8("DblClOnTorFn"), getActionOnDblClOnTorrentFn()); settings.setValue(QString::fromUtf8("DblClOnTorFn"), getActionOnDblClOnTorrentFn());
// End Downloads preferences // End Downloads preferences
@ -627,6 +631,19 @@ void options_imp::loadOptions(){
textScanDir->setText(strValue); textScanDir->setText(strValue);
enableDirScan(checkScanDir->isChecked()); 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(); intValue = Preferences::getActionOnDblClOnTorrentDl();
if(intValue >= actionTorrentDlOnDblClBox->count()) if(intValue >= actionTorrentDlOnDblClBox->count())
intValue = 0; intValue = 0;
@ -1267,6 +1284,11 @@ void options_imp::enableDirScan(bool checked){
browseScanDirButton->setEnabled(checked); browseScanDirButton->setEnabled(checked);
} }
void options_imp::enableTorrentExport(bool checked) {
textExportDir->setEnabled(checked);
browseExportDirButton->setEnabled(checked);
}
bool options_imp::isSlashScreenDisabled() const { bool options_imp::isSlashScreenDisabled() const {
return checkNoSplash->isChecked(); 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 // Return action on double-click on a downloading torrent set in options
int options_imp::getActionOnDblClOnTorrentDl() const { int options_imp::getActionOnDblClOnTorrentDl() const {
if(actionTorrentDlOnDblClBox->currentIndex()<1) if(actionTorrentDlOnDblClBox->currentIndex()<1)

2
src/options_imp.h

@ -83,6 +83,7 @@ protected:
bool addTorrentsInPause() const; bool addTorrentsInPause() const;
bool isDirScanEnabled() const; bool isDirScanEnabled() const;
QString getScanDir() const; QString getScanDir() const;
QString getExportDir() const;
int getActionOnDblClOnTorrentDl() const; int getActionOnDblClOnTorrentDl() const;
int getActionOnDblClOnTorrentFn() const; int getActionOnDblClOnTorrentFn() const;
// Connection options // Connection options
@ -135,6 +136,7 @@ protected slots:
void enableDownloadLimit(bool checked); void enableDownloadLimit(bool checked);
void enableTempPathInput(bool checked); void enableTempPathInput(bool checked);
void enableDirScan(bool checked); void enableDirScan(bool checked);
void enableTorrentExport(bool checked);
void enablePeerProxy(int comboIndex); void enablePeerProxy(int comboIndex);
void enablePeerProxyAuth(bool checked); void enablePeerProxyAuth(bool checked);
void enableHTTPProxy(int comboIndex); void enableHTTPProxy(int comboIndex);

18
src/preferences.h

@ -214,6 +214,24 @@ public:
settings.setValue(QString::fromUtf8("Preferences/Downloads/ScanDir"), path); 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() { static int getActionOnDblClOnTorrentDl() {
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
return settings.value(QString::fromUtf8("Preferences/Downloads/DblClOnTorDl"), 0).toInt(); return settings.value(QString::fromUtf8("Preferences/Downloads/DblClOnTorDl"), 0).toInt();

5
src/transferlistwidget.cpp

@ -581,7 +581,10 @@ void TransferListWidget::torrentDoubleClicked(QModelIndex index) {
} }
break; break;
case OPEN_DEST: 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; break;
} }
} }

63
src/ui/options.ui

@ -629,7 +629,7 @@ QGroupBox {
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>644</width> <width>644</width>
<height>504</height> <height>583</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_17"> <layout class="QVBoxLayout" name="verticalLayout_17">
@ -645,9 +645,6 @@ QGroupBox {
<string>File system</string> <string>File system</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_12"> <layout class="QVBoxLayout" name="verticalLayout_12">
<property name="spacing">
<number>3</number>
</property>
<item> <item>
<widget class="QGroupBox" name="groupBox_3"> <widget class="QGroupBox" name="groupBox_3">
<property name="styleSheet"> <property name="styleSheet">
@ -811,6 +808,52 @@ QGroupBox {
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QCheckBox" name="checkExportDir">
<property name="text">
<string>Copy .torrent files to:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_15">
<item>
<widget class="QLineEdit" name="textExportDir">
<property name="enabled">
<bool>false</bool>
</property>
<property name="styleSheet">
<string>QLineEdit {
margin-left: 23px;
}</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="browseExportDirButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>25</width>
<height>27</height>
</size>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/Icons/oxygen/browse.png</normaloff>:/Icons/oxygen/browse.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item> <item>
<widget class="QCheckBox" name="checkAppendqB"> <widget class="QCheckBox" name="checkAppendqB">
<property name="text"> <property name="text">
@ -1393,8 +1436,8 @@ QGroupBox {
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>620</width> <width>366</width>
<height>490</height> <height>332</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_33"> <layout class="QVBoxLayout" name="verticalLayout_33">
@ -1798,8 +1841,8 @@ QGroupBox {
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>620</width> <width>466</width>
<height>490</height> <height>415</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_20"> <layout class="QVBoxLayout" name="verticalLayout_20">
@ -2746,8 +2789,8 @@ QGroupBox {
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>219</width> <width>620</width>
<height>221</height> <height>490</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_23"> <layout class="QVBoxLayout" name="verticalLayout_23">

Loading…
Cancel
Save