mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-02-02 18:04:32 +00:00
Merge pull request #5465 from glassez/strip_root_folder_v2
Allow to strip root folder. Closes #588, closes #5433
This commit is contained in:
commit
d702b32e53
@ -244,6 +244,7 @@ Session::Session(QObject *parent)
|
||||
, m_additionalTrackers(BITTORRENT_SESSION_KEY("AdditionalTrackers"))
|
||||
, m_globalMaxRatio(BITTORRENT_SESSION_KEY("GlobalMaxRatio"), -1, [](qreal r) { return r < 0 ? -1. : r;})
|
||||
, m_isAddTorrentPaused(BITTORRENT_SESSION_KEY("AddTorrentPaused"), false)
|
||||
, m_isCreateTorrentSubfolder(BITTORRENT_SESSION_KEY("CreateTorrentSubfolder"), true)
|
||||
, m_isAppendExtensionEnabled(BITTORRENT_SESSION_KEY("AddExtensionToIncompleteFiles"), false)
|
||||
, m_refreshInterval(BITTORRENT_SESSION_KEY("RefreshInterval"), 1500)
|
||||
, m_isPreallocationEnabled(BITTORRENT_SESSION_KEY("Preallocation"), false)
|
||||
@ -1405,7 +1406,9 @@ bool Session::deleteTorrent(const QString &hash, bool deleteLocalFiles)
|
||||
|
||||
// Remove it from session
|
||||
if (deleteLocalFiles) {
|
||||
m_savePathsToRemove[torrent->hash()] = torrent->rootPath(true);
|
||||
QString rootPath = torrent->rootPath(true);
|
||||
if (!rootPath.isEmpty())
|
||||
m_savePathsToRemove[torrent->hash()] = rootPath;
|
||||
m_nativeSession->remove_torrent(torrent->nativeHandle(), libt::session::delete_files);
|
||||
}
|
||||
else {
|
||||
@ -1652,6 +1655,9 @@ bool Session::addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri
|
||||
p = magnetUri.addTorrentParams();
|
||||
}
|
||||
else if (torrentInfo.isValid()) {
|
||||
if (!addData.resumed && !addData.hasRootFolder)
|
||||
torrentInfo.stripRootFolder();
|
||||
|
||||
// Metadata
|
||||
if (!addData.resumed && !addData.hasSeedStatus)
|
||||
findIncompleteFiles(torrentInfo, savePath);
|
||||
@ -3248,6 +3254,16 @@ void Session::getPendingAlerts(std::vector<libt::alert *> &out, ulong time)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Session::isCreateTorrentSubfolder() const
|
||||
{
|
||||
return m_isCreateTorrentSubfolder;
|
||||
}
|
||||
|
||||
void Session::setCreateTorrentSubfolder(bool value)
|
||||
{
|
||||
m_isCreateTorrentSubfolder = value;
|
||||
}
|
||||
|
||||
// Read alerts sent by the BitTorrent session
|
||||
void Session::readAlerts()
|
||||
{
|
||||
@ -3646,6 +3662,7 @@ namespace
|
||||
torrentData.name = QString::fromStdString(fast.dict_find_string_value("qBt-name"));
|
||||
torrentData.hasSeedStatus = fast.dict_find_int_value("qBt-seedStatus");
|
||||
torrentData.disableTempPath = fast.dict_find_int_value("qBt-tempPathDisabled");
|
||||
torrentData.hasRootFolder = fast.dict_find_int_value("qBt-hasRootFolder");
|
||||
|
||||
magnetUri = MagnetUri(QString::fromStdString(fast.dict_find_string_value("qBt-magnetUri")));
|
||||
torrentData.addPaused = fast.dict_find_int_value("qBt-paused");
|
||||
|
@ -147,6 +147,7 @@ namespace BitTorrent
|
||||
QVector<int> filePriorities; // used if TorrentInfo is set
|
||||
bool ignoreShareRatio = false;
|
||||
bool skipChecking = false;
|
||||
bool createSubfolder = true;
|
||||
};
|
||||
|
||||
struct TorrentStatusReport
|
||||
@ -221,6 +222,8 @@ namespace BitTorrent
|
||||
void setPeXEnabled(bool enabled);
|
||||
bool isAddTorrentPaused() const;
|
||||
void setAddTorrentPaused(bool value);
|
||||
bool isCreateTorrentSubfolder() const;
|
||||
void setCreateTorrentSubfolder(bool value);
|
||||
bool isTrackerEnabled() const;
|
||||
void setTrackerEnabled(bool enabled);
|
||||
bool isAppendExtensionEnabled() const;
|
||||
@ -538,6 +541,7 @@ namespace BitTorrent
|
||||
CachedSettingValue<QString> m_additionalTrackers;
|
||||
CachedSettingValue<qreal> m_globalMaxRatio;
|
||||
CachedSettingValue<bool> m_isAddTorrentPaused;
|
||||
CachedSettingValue<bool> m_isCreateTorrentSubfolder;
|
||||
CachedSettingValue<bool> m_isAppendExtensionEnabled;
|
||||
CachedSettingValue<uint> m_refreshInterval;
|
||||
CachedSettingValue<bool> m_isPreallocationEnabled;
|
||||
|
@ -89,6 +89,7 @@ AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms)
|
||||
, sequential(params.sequential)
|
||||
, hasSeedStatus(params.skipChecking) // do not react on 'torrent_finished_alert' when skipping
|
||||
, skipChecking(params.skipChecking)
|
||||
, hasRootFolder(params.createSubfolder)
|
||||
, addForced(params.addForced)
|
||||
, addPaused(params.addPaused)
|
||||
, filePriorities(params.filePriorities)
|
||||
@ -199,6 +200,7 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
|
||||
, m_ratioLimit(data.ratioLimit)
|
||||
, m_tempPathDisabled(data.disableTempPath)
|
||||
, m_hasMissingFiles(false)
|
||||
, m_hasRootFolder(data.hasRootFolder)
|
||||
, m_pauseAfterRecheck(false)
|
||||
, m_needSaveResumeData(false)
|
||||
{
|
||||
@ -208,8 +210,13 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
|
||||
updateStatus();
|
||||
m_hash = InfoHash(m_nativeStatus.info_hash);
|
||||
|
||||
if (!data.resumed)
|
||||
if (!data.resumed) {
|
||||
setSequentialDownload(data.sequential);
|
||||
if (hasMetadata()) {
|
||||
if (filesCount() == 1)
|
||||
m_hasRootFolder = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TorrentHandle::~TorrentHandle() {}
|
||||
@ -230,6 +237,9 @@ QString TorrentHandle::name() const
|
||||
if (name.isEmpty())
|
||||
name = QString::fromStdString(m_nativeStatus.name);
|
||||
|
||||
if (name.isEmpty() && hasMetadata())
|
||||
name = QString::fromStdString(m_torrentInfo.nativeInfo()->orig_files().name());
|
||||
|
||||
if (name.isEmpty())
|
||||
name = m_hash;
|
||||
|
||||
@ -302,6 +312,9 @@ QString TorrentHandle::savePath(bool actual) const
|
||||
|
||||
QString TorrentHandle::rootPath(bool actual) const
|
||||
{
|
||||
if ((filesCount() > 1) && !hasRootFolder())
|
||||
return QString();
|
||||
|
||||
QString firstFilePath = filePath(0);
|
||||
const int slashIndex = firstFilePath.indexOf("/");
|
||||
if (slashIndex >= 0)
|
||||
@ -314,8 +327,10 @@ QString TorrentHandle::contentPath(bool actual) const
|
||||
{
|
||||
if (filesCount() == 1)
|
||||
return QDir(savePath(actual)).absoluteFilePath(filePath(0));
|
||||
else
|
||||
else if (hasRootFolder())
|
||||
return rootPath(actual);
|
||||
else
|
||||
return savePath(actual);
|
||||
}
|
||||
|
||||
bool TorrentHandle::isAutoTMMEnabled() const
|
||||
@ -334,6 +349,11 @@ void TorrentHandle::setAutoTMMEnabled(bool enabled)
|
||||
move_impl(m_session->categorySavePath(m_category));
|
||||
}
|
||||
|
||||
bool TorrentHandle::hasRootFolder() const
|
||||
{
|
||||
return m_hasRootFolder;
|
||||
}
|
||||
|
||||
QString TorrentHandle::nativeActualSavePath() const
|
||||
{
|
||||
return QString::fromStdString(m_nativeStatus.save_path);
|
||||
@ -1489,6 +1509,7 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
|
||||
resumeData["qBt-seedStatus"] = m_hasSeedStatus;
|
||||
resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
|
||||
resumeData["qBt-queuePosition"] = queuePosition();
|
||||
resumeData["qBt-hasRootFolder"] = m_hasRootFolder;
|
||||
|
||||
m_session->handleTorrentResumeDataReady(this, resumeData);
|
||||
}
|
||||
@ -1590,6 +1611,10 @@ void TorrentHandle::handleMetadataReceivedAlert(libt::metadata_received_alert *p
|
||||
updateStatus();
|
||||
if (m_session->isAppendExtensionEnabled())
|
||||
manageIncompleteFiles();
|
||||
if (!m_hasRootFolder)
|
||||
m_torrentInfo.stripRootFolder();
|
||||
if (filesCount() == 1)
|
||||
m_hasRootFolder = false;
|
||||
m_session->handleTorrentMetadataReceived(this);
|
||||
|
||||
if (isPaused()) {
|
||||
|
@ -98,6 +98,7 @@ namespace BitTorrent
|
||||
bool sequential;
|
||||
bool hasSeedStatus;
|
||||
bool skipChecking;
|
||||
bool hasRootFolder;
|
||||
TriStateBool addForced;
|
||||
TriStateBool addPaused;
|
||||
// for new torrents
|
||||
@ -206,6 +207,9 @@ namespace BitTorrent
|
||||
// file4
|
||||
//
|
||||
//
|
||||
// Torrent A* (Torrent A in "strip root folder" mode)
|
||||
//
|
||||
//
|
||||
// Torrent B (singlefile)
|
||||
//
|
||||
// torrentB/
|
||||
@ -222,6 +226,7 @@ namespace BitTorrent
|
||||
// | | rootPath | contentPath |
|
||||
// |---|------------------------------|--------------------------------------------|
|
||||
// | A | /home/user/torrents/torrentA | /home/user/torrents/torrentA |
|
||||
// | A*| <empty> | /home/user/torrents |
|
||||
// | B | /home/user/torrents/torrentB | /home/user/torrents/torrentB/subdir1/file1 |
|
||||
// | C | /home/user/torrents/file1 | /home/user/torrents/file1 |
|
||||
|
||||
@ -235,6 +240,8 @@ namespace BitTorrent
|
||||
bool belongsToCategory(const QString &category) const;
|
||||
bool setCategory(const QString &category);
|
||||
|
||||
bool hasRootFolder() const;
|
||||
|
||||
int filesCount() const;
|
||||
int piecesCount() const;
|
||||
int piecesHave() const;
|
||||
@ -421,6 +428,7 @@ namespace BitTorrent
|
||||
qreal m_ratioLimit;
|
||||
bool m_tempPathDisabled;
|
||||
bool m_hasMissingFiles;
|
||||
bool m_hasRootFolder;
|
||||
|
||||
bool m_pauseAfterRecheck;
|
||||
bool m_needSaveResumeData;
|
||||
|
@ -279,7 +279,7 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(int fileIndex) const
|
||||
void TorrentInfo::renameFile(uint index, const QString &newPath)
|
||||
{
|
||||
if (!isValid()) return;
|
||||
nativeInfo()->rename_file(index, newPath.toStdString());
|
||||
nativeInfo()->rename_file(index, Utils::Fs::toNativePath(newPath).toStdString());
|
||||
}
|
||||
|
||||
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
||||
@ -293,6 +293,24 @@ int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TorrentInfo::stripRootFolder()
|
||||
{
|
||||
if (filesCount() <= 1) return;
|
||||
|
||||
libtorrent::file_storage files = m_nativeInfo->files();
|
||||
|
||||
// Solution for case of renamed root folder
|
||||
std::string testName = filePath(0).split('/').value(0).toStdString();
|
||||
if (files.name() != testName) {
|
||||
files.set_name(testName);
|
||||
for (int i = 0; i < files.num_files(); ++i)
|
||||
files.rename_file(i, files.file_path(i));
|
||||
}
|
||||
|
||||
files.set_name("");
|
||||
m_nativeInfo->remap_files(files);
|
||||
}
|
||||
|
||||
TorrentInfo::NativePtr TorrentInfo::nativeInfo() const
|
||||
{
|
||||
return m_nativeInfo;
|
||||
|
@ -99,6 +99,7 @@ namespace BitTorrent
|
||||
PieceRange filePieces(int fileIndex) const;
|
||||
|
||||
void renameFile(uint index, const QString &newPath);
|
||||
void stripRootFolder();
|
||||
|
||||
NativePtr nativeInfo() const;
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <QMenu>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "base/preferences.h"
|
||||
#include "base/settingsstorage.h"
|
||||
#include "base/net/downloadmanager.h"
|
||||
#include "base/net/downloadhandler.h"
|
||||
@ -95,6 +96,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(QWidget *parent)
|
||||
connect(ui->savePathComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onSavePathChanged(int)));
|
||||
connect(ui->browseButton, SIGNAL(clicked()), SLOT(browseButton_clicked()));
|
||||
ui->defaultSavePathCheckBox->setVisible(false); // Default path is selected by default
|
||||
ui->createSubfolderCheckBox->setChecked(session->isCreateTorrentSubfolder());
|
||||
|
||||
ui->doNotDeleteTorrentCheckBox->setVisible(TorrentFileGuard::autoDeleteMode() != TorrentFileGuard::Never);
|
||||
|
||||
@ -624,9 +626,8 @@ void AddNewTorrentDialog::accept()
|
||||
|
||||
BitTorrent::AddTorrentParams params;
|
||||
|
||||
if (ui->skip_check_cb->isChecked())
|
||||
// TODO: Check if destination actually exists
|
||||
params.skipChecking = true;
|
||||
params.skipChecking = ui->skipCheckingCheckBox->isChecked();
|
||||
|
||||
// Category
|
||||
params.category = ui->categoryComboBox->currentText();
|
||||
@ -639,6 +640,7 @@ void AddNewTorrentDialog::accept()
|
||||
params.filePriorities = m_contentModel->model()->getFilePriorities();
|
||||
|
||||
params.addPaused = !ui->startTorrentCheckBox->isChecked();
|
||||
params.createSubfolder = ui->createSubfolderCheckBox->isChecked();
|
||||
|
||||
QString savePath = ui->savePathComboBox->itemData(ui->savePathComboBox->currentIndex()).toString();
|
||||
if (ui->comboTTM->currentIndex() != 1) { // 0 is Manual mode and 1 is Automatic mode. Handle all non 1 values as manual mode.
|
||||
|
@ -170,7 +170,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="skip_check_cb">
|
||||
<widget class="QCheckBox" name="skipCheckingCheckBox">
|
||||
<property name="text">
|
||||
<string>Skip hash check</string>
|
||||
</property>
|
||||
@ -192,6 +192,26 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="startTorrentCheckBox_2">
|
||||
<property name="text">
|
||||
<string>Start torrent</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="createSubfolderCheckBox">
|
||||
<property name="text">
|
||||
<string>Create subfolder</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -285,8 +305,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>308</width>
|
||||
<height>74</height>
|
||||
<width>321</width>
|
||||
<height>69</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@ -403,7 +423,7 @@
|
||||
<tabstop>never_show_cb</tabstop>
|
||||
<tabstop>adv_button</tabstop>
|
||||
<tabstop>startTorrentCheckBox</tabstop>
|
||||
<tabstop>skip_check_cb</tabstop>
|
||||
<tabstop>skipCheckingCheckBox</tabstop>
|
||||
<tabstop>categoryComboBox</tabstop>
|
||||
<tabstop>defaultCategoryCheckbox</tabstop>
|
||||
<tabstop>scrollArea</tabstop>
|
||||
|
@ -214,6 +214,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||
connect(m_ui->checkAdditionDialog, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
|
||||
connect(m_ui->checkAdditionDialogFront, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
|
||||
connect(m_ui->checkStartPaused, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
|
||||
connect(m_ui->checkCreateSubfolder, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
|
||||
connect(m_ui->deleteTorrentBox, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
|
||||
connect(m_ui->deleteCancelledTorrentBox, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
|
||||
connect(m_ui->checkExportDir, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton()));
|
||||
@ -515,6 +516,7 @@ void OptionsDialog::saveOptions()
|
||||
AddNewTorrentDialog::setEnabled(useAdditionDialog());
|
||||
AddNewTorrentDialog::setTopLevel(m_ui->checkAdditionDialogFront->isChecked());
|
||||
session->setAddTorrentPaused(addTorrentsInPause());
|
||||
session->setCreateTorrentSubfolder(m_ui->checkCreateSubfolder->isChecked());
|
||||
ScanFoldersModel::instance()->removeFromFSWatcher(removedScanDirs);
|
||||
ScanFoldersModel::instance()->addToFSWatcher(addedScanDirs);
|
||||
ScanFoldersModel::instance()->makePersistent();
|
||||
@ -716,6 +718,7 @@ void OptionsDialog::loadOptions()
|
||||
m_ui->checkAdditionDialog->setChecked(AddNewTorrentDialog::isEnabled());
|
||||
m_ui->checkAdditionDialogFront->setChecked(AddNewTorrentDialog::isTopLevel());
|
||||
m_ui->checkStartPaused->setChecked(session->isAddTorrentPaused());
|
||||
m_ui->checkCreateSubfolder->setChecked(session->isCreateTorrentSubfolder());
|
||||
const TorrentFileGuard::AutoDeleteMode autoDeleteMode = TorrentFileGuard::autoDeleteMode();
|
||||
m_ui->deleteTorrentBox->setChecked(autoDeleteMode != TorrentFileGuard::Never);
|
||||
m_ui->deleteCancelledTorrentBox->setChecked(autoDeleteMode == TorrentFileGuard::Always);
|
||||
|
@ -709,6 +709,16 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="checkCreateSubfolder">
|
||||
<property name="text">
|
||||
<string>Create subfolder for torrents with multiple files</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkStartPaused">
|
||||
<property name="text">
|
||||
|
@ -316,6 +316,7 @@ void PropertiesWidget::loadTorrentInfos(BitTorrent::TorrentHandle *const torrent
|
||||
|
||||
// List files in torrent
|
||||
PropListModel->model()->setupModelData(m_torrent->info());
|
||||
if ((m_torrent->filesCount() > 1) && (PropListModel->model()->rowCount() == 1))
|
||||
filesList->setExpanded(PropListModel->index(0, 0), true);
|
||||
|
||||
// Load file priorities
|
||||
|
Loading…
x
Reference in New Issue
Block a user