diff --git a/WebAPI_Changelog.md b/WebAPI_Changelog.md index 0c60f7736..3bfe4356c 100644 --- a/WebAPI_Changelog.md +++ b/WebAPI_Changelog.md @@ -1,5 +1,9 @@ # WebAPI Changelog +## 2.12.1 +* [#23031](https://github.com/qbittorrent/qBittorrent/pull/23031) + * Add `torrents/setComment` endpoint with parameters `hashes` and `comment` for setting a new torrent comment + ## 2.12.0 * [#22989](https://github.com/qbittorrent/qBittorrent/pull/22989) diff --git a/src/base/bittorrent/bencoderesumedatastorage.cpp b/src/base/bittorrent/bencoderesumedatastorage.cpp index 87b349f14..ee87b5601 100644 --- a/src/base/bittorrent/bencoderesumedatastorage.cpp +++ b/src/base/bittorrent/bencoderesumedatastorage.cpp @@ -236,6 +236,7 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre LoadTorrentParams torrentParams; torrentParams.category = fromLTString(resumeDataRoot.dict_find_string_value("qBt-category")); torrentParams.name = fromLTString(resumeDataRoot.dict_find_string_value("qBt-name")); + torrentParams.comment = fromLTString(resumeDataRoot.dict_find_string_value("qBt-comment")); torrentParams.hasFinishedStatus = resumeDataRoot.dict_find_int_value("qBt-seedStatus"); torrentParams.firstLastPiecePriority = resumeDataRoot.dict_find_int_value("qBt-firstLastPiecePriority"); torrentParams.seedingTimeLimit = resumeDataRoot.dict_find_int_value("qBt-seedingTimeLimit", Torrent::USE_GLOBAL_SEEDING_TIME); @@ -437,6 +438,7 @@ void BitTorrent::BencodeResumeDataStorage::Worker::store(const TorrentID &id, co data["qBt-category"] = resumeData.category.toStdString(); data["qBt-tags"] = setToEntryList(resumeData.tags); data["qBt-name"] = resumeData.name.toStdString(); + data["qBt-comment"] = resumeData.comment.toStdString(); data["qBt-seedStatus"] = resumeData.hasFinishedStatus; data["qBt-contentLayout"] = Utils::String::fromEnum(resumeData.contentLayout).toStdString(); data["qBt-firstLastPiecePriority"] = resumeData.firstLastPiecePriority; diff --git a/src/base/bittorrent/dbresumedatastorage.cpp b/src/base/bittorrent/dbresumedatastorage.cpp index 64021d671..4c6a3576c 100644 --- a/src/base/bittorrent/dbresumedatastorage.cpp +++ b/src/base/bittorrent/dbresumedatastorage.cpp @@ -67,7 +67,7 @@ namespace { const QString DB_CONNECTION_NAME = u"ResumeDataStorage"_s; - const int DB_VERSION = 8; + const int DB_VERSION = 9; const QString DB_TABLE_META = u"meta"_s; const QString DB_TABLE_TORRENTS = u"torrents"_s; @@ -131,6 +131,7 @@ namespace const Column DB_COLUMN_NAME = makeColumn(u"name"_s); const Column DB_COLUMN_CATEGORY = makeColumn(u"category"_s); const Column DB_COLUMN_TAGS = makeColumn(u"tags"_s); + const Column DB_COLUMN_COMMENT = makeColumn(u"comment"_s); const Column DB_COLUMN_TARGET_SAVE_PATH = makeColumn(u"target_save_path"_s); const Column DB_COLUMN_DOWNLOAD_PATH = makeColumn(u"download_path"_s); const Column DB_COLUMN_CONTENT_LAYOUT = makeColumn(u"content_layout"_s); @@ -461,6 +462,7 @@ void BitTorrent::DBResumeDataStorage::createDB() const makeColumnDefinition(DB_COLUMN_NAME, u"TEXT"_s), makeColumnDefinition(DB_COLUMN_CATEGORY, u"TEXT"_s), makeColumnDefinition(DB_COLUMN_TAGS, u"TEXT"_s), + makeColumnDefinition(DB_COLUMN_COMMENT, u"TEXT"_s), makeColumnDefinition(DB_COLUMN_TARGET_SAVE_PATH, u"TEXT"_s), makeColumnDefinition(DB_COLUMN_DOWNLOAD_PATH, u"TEXT"_s), makeColumnDefinition(DB_COLUMN_CONTENT_LAYOUT, u"TEXT NOT NULL"_s), @@ -578,6 +580,9 @@ void BitTorrent::DBResumeDataStorage::updateDB(const int fromVersion) const throw RuntimeError(query.lastError().text()); } + if (fromVersion <= 8) + addColumn(DB_TABLE_TORRENTS, DB_COLUMN_COMMENT, u"TEXT"_s); + const QString updateMetaVersionQuery = makeUpdateStatement(DB_TABLE_META, {DB_COLUMN_NAME, DB_COLUMN_VALUE}); if (!query.prepare(updateMetaVersionQuery)) throw RuntimeError(query.lastError().text()); @@ -619,6 +624,7 @@ LoadResumeDataResult DBResumeDataStorage::parseQueryResultRow(const QSqlQuery &q LoadTorrentParams resumeData; resumeData.name = query.value(DB_COLUMN_NAME.name).toString(); resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString(); + resumeData.comment = query.value(DB_COLUMN_COMMENT.name).toString(); const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString(); if (!tagsData.isEmpty()) { @@ -834,6 +840,7 @@ StoreJob::StoreJob(const TorrentID &torrentID, LoadTorrentParams resumeData) DB_COLUMN_NAME, DB_COLUMN_CATEGORY, DB_COLUMN_TAGS, + DB_COLUMN_COMMENT, DB_COLUMN_TARGET_SAVE_PATH, DB_COLUMN_DOWNLOAD_PATH, DB_COLUMN_CONTENT_LAYOUT, @@ -899,6 +906,7 @@ StoreJob::StoreJob(const TorrentID &torrentID, LoadTorrentParams resumeData) query.bindValue(DB_COLUMN_CATEGORY.placeholder, m_resumeData.category); query.bindValue(DB_COLUMN_TAGS.placeholder, (m_resumeData.tags.isEmpty() ? QString() : Utils::String::joinIntoString(m_resumeData.tags, u","_s))); + query.bindValue(DB_COLUMN_COMMENT.placeholder, m_resumeData.comment); query.bindValue(DB_COLUMN_CONTENT_LAYOUT.placeholder, Utils::String::fromEnum(m_resumeData.contentLayout)); query.bindValue(DB_COLUMN_RATIO_LIMIT.placeholder, static_cast(m_resumeData.ratioLimit * 1000)); query.bindValue(DB_COLUMN_SEEDING_TIME_LIMIT.placeholder, m_resumeData.seedingTimeLimit); diff --git a/src/base/bittorrent/loadtorrentparams.h b/src/base/bittorrent/loadtorrentparams.h index b84a9eb41..24ebc10d1 100644 --- a/src/base/bittorrent/loadtorrentparams.h +++ b/src/base/bittorrent/loadtorrentparams.h @@ -50,6 +50,7 @@ namespace BitTorrent TagSet tags; Path savePath; Path downloadPath; + QString comment; TorrentContentLayout contentLayout = TorrentContentLayout::Original; TorrentOperatingMode operatingMode = TorrentOperatingMode::AutoManaged; bool useAutoTMM = false; diff --git a/src/base/bittorrent/torrent.h b/src/base/bittorrent/torrent.h index 644af158b..c47036147 100644 --- a/src/base/bittorrent/torrent.h +++ b/src/base/bittorrent/torrent.h @@ -144,6 +144,7 @@ namespace BitTorrent virtual QDateTime creationDate() const = 0; virtual QString creator() const = 0; virtual QString comment() const = 0; + virtual void setComment(const QString &comment) = 0; virtual bool isPrivate() const = 0; virtual qlonglong totalSize() const = 0; virtual qlonglong wantedSize() const = 0; diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index c3a7c0f2b..1c101d0be 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -367,6 +367,9 @@ TorrentImpl::TorrentImpl(SessionImpl *session, const lt::torrent_handle &nativeH } } + if (!params.comment.isEmpty()) + m_comment = params.comment; + setStopCondition(params.stopCondition); const auto *extensionData = static_cast(m_ltAddTorrentParams.userdata); @@ -440,6 +443,15 @@ QString TorrentImpl::comment() const return m_comment; } +void TorrentImpl::setComment(const QString &comment) +{ + if (m_comment != comment) + { + m_comment = comment; + deferredRequestResumeData(); + } +} + bool TorrentImpl::isPrivate() const { return m_torrentInfo.isPrivate(); @@ -2210,6 +2222,7 @@ void TorrentImpl::prepareResumeData(lt::add_torrent_params params) .tags = m_tags, .savePath = (!m_useAutoTMM ? m_savePath : Path()), .downloadPath = (!m_useAutoTMM ? m_downloadPath : Path()), + .comment = m_comment, .contentLayout = m_contentLayout, .operatingMode = m_operatingMode, .useAutoTMM = m_useAutoTMM, diff --git a/src/base/bittorrent/torrentimpl.h b/src/base/bittorrent/torrentimpl.h index 2c1f9af77..e44d875c9 100644 --- a/src/base/bittorrent/torrentimpl.h +++ b/src/base/bittorrent/torrentimpl.h @@ -106,6 +106,7 @@ namespace BitTorrent QDateTime creationDate() const override; QString creator() const override; QString comment() const override; + void setComment(const QString &comment) override; bool isPrivate() const override; qlonglong totalSize() const override; qlonglong wantedSize() const override; diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index d0c5f54af..bb210bffd 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -1591,6 +1591,19 @@ void TorrentsController::renameAction() setResult(QString()); } +void TorrentsController::setCommentAction() +{ + requireParams({u"hashes"_s, u"comment"_s}); + + const QStringList hashes {params()[u"hashes"_s].split(u'|')}; + const QString comment = params()[u"comment"_s].trimmed(); + + applyToTorrents(hashes, [&comment](BitTorrent::Torrent *const torrent) + { + torrent->setComment(comment); + }); +} + void TorrentsController::setAutoManagementAction() { requireParams({u"hashes"_s, u"enable"_s}); diff --git a/src/webui/api/torrentscontroller.h b/src/webui/api/torrentscontroller.h index 2792c835c..7f81ec15a 100644 --- a/src/webui/api/torrentscontroller.h +++ b/src/webui/api/torrentscontroller.h @@ -71,6 +71,7 @@ private slots: void recheckAction(); void reannounceAction(); void renameAction(); + void setCommentAction(); void setCategoryAction(); void createCategoryAction(); void editCategoryAction(); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index da3d0211d..7c7fc15fe 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -53,7 +53,7 @@ #include "base/utils/version.h" #include "api/isessionmanager.h" -inline const Utils::Version<3, 2> API_VERSION {2, 12, 0}; +inline const Utils::Version<3, 2> API_VERSION {2, 12, 1}; class APIController; class AuthController; @@ -200,6 +200,7 @@ private: {{u"torrents"_s, u"renameFolder"_s}, Http::METHOD_POST}, {{u"torrents"_s, u"setAutoManagement"_s}, Http::METHOD_POST}, {{u"torrents"_s, u"setCategory"_s}, Http::METHOD_POST}, + {{u"torrents"_s, u"setComment"_s}, Http::METHOD_POST}, {{u"torrents"_s, u"setDownloadLimit"_s}, Http::METHOD_POST}, {{u"torrents"_s, u"setDownloadPath"_s}, Http::METHOD_POST}, {{u"torrents"_s, u"setForceStart"_s}, Http::METHOD_POST},